├── .classpath ├── .gitignore ├── .project ├── .settings ├── org.eclipse.core.resources.prefs ├── org.eclipse.jdt.core.prefs └── org.eclipse.m2e.core.prefs ├── ARTICLE1.md ├── ARTICLE2.md ├── HEADER-DERRIVED.txt ├── HEADER-ORIGINAL.txt ├── LICENSE ├── README.md ├── examples └── net │ └── tascalate │ └── concurrent │ └── ExecutorAwareThread.java ├── pom.xml └── src ├── main ├── java │ ├── module-info.java │ └── net │ │ └── tascalate │ │ └── concurrent │ │ ├── AbstractCompletableTask.java │ │ ├── AbstractThreadFactoryBuilder.java │ │ ├── AggregatingPromise.java │ │ ├── AsyncCloseable.java │ │ ├── AsyncCompletions.java │ │ ├── AsyncLoop.java │ │ ├── CallbackRegistry.java │ │ ├── CompletableFutureWrapper.java │ │ ├── CompletablePromise.java │ │ ├── CompletableSubTask.java │ │ ├── CompletableTask.java │ │ ├── CompletionStageRef.java │ │ ├── CompletionStageWrapper.java │ │ ├── ConfigurableDependentPromise.java │ │ ├── DelayPolicy.java │ │ ├── DependentPromise.java │ │ ├── MultitargetException.java │ │ ├── PaddedOutputStream.java │ │ ├── PaddedWriter.java │ │ ├── Promise.java │ │ ├── PromiseAdapter.java │ │ ├── PromiseAdapterExtended.java │ │ ├── PromiseHelper.java │ │ ├── PromiseOperations.java │ │ ├── PromiseOrigin.java │ │ ├── Promises.java │ │ ├── RetryCallable.java │ │ ├── RetryContext.java │ │ ├── RetryException.java │ │ ├── RetryPolicy.java │ │ ├── RetryRunnable.java │ │ ├── RunnablePromise.java │ │ ├── SharedFunctions.java │ │ ├── TaskCompletionService.java │ │ ├── TaskExecutorCompletionService.java │ │ ├── TaskExecutorService.java │ │ ├── TaskExecutors.java │ │ ├── ThreadFactoryBuilder.java │ │ ├── ThreadGroupBuilder.java │ │ ├── ThreadPoolTaskExecutor.java │ │ ├── Timeouts.java │ │ ├── Try.java │ │ ├── core │ │ ├── CancelMethodsCache.java │ │ ├── CompletionStageAPI.java │ │ ├── CurrentCompletionStageAPI.java │ │ ├── Decorator.java │ │ ├── FunctionMemoization.java │ │ ├── J12CompletionStageAPI.java │ │ ├── J8CompletionStageAPI.java │ │ ├── J9CompletionStageAPI.java │ │ ├── KeyedLocks.java │ │ └── ReferenceType.java │ │ ├── decorators │ │ ├── AbstractCompletionStageDecorator.java │ │ ├── AbstractDependentPromiseDecorator.java │ │ ├── AbstractFutureDecorator.java │ │ ├── AbstractPromiseDecorator.java │ │ ├── AbstractPromiseLikeDecorator.java │ │ ├── BlockingCompletionStageDecorator.java │ │ ├── CompletableFutureDecorator.java │ │ ├── CompletionStageDecorator.java │ │ ├── CustomizableDependentPromiseDecorator.java │ │ ├── CustomizablePromiseDecorator.java │ │ ├── ExecutorBoundCompletionStage.java │ │ ├── ExecutorBoundDependentPromise.java │ │ ├── ExecutorBoundPromise.java │ │ ├── ExtendedDependentPromiseDecorator.java │ │ ├── ExtendedPromiseDecorator.java │ │ └── PromiseCustomizer.java │ │ ├── delays │ │ ├── BoundedMaxDelayPolicy.java │ │ ├── BoundedMinDelayPolicy.java │ │ ├── DelayPolicyWrapper.java │ │ ├── DurationCalcs.java │ │ ├── ExponentialDelayPolicy.java │ │ ├── FirstRetryNoDelayPolicy.java │ │ ├── FixedIntervalDelayPolicy.java │ │ ├── OnFailureNoDelayPolicy.java │ │ ├── OnSuccessNoDelayPolicy.java │ │ ├── ProportionalRandomDelayPolicy.java │ │ ├── RandomDelayPolicy.java │ │ └── UniformRandomDelayPolicy.java │ │ ├── io │ │ ├── AbstractAsyncFileChannel.java │ │ ├── AbstractAsyncServerSocketChannel.java │ │ ├── AbstractAsyncSocketChannel.java │ │ ├── AsyncByteChannel.java │ │ ├── AsyncChannel.java │ │ ├── AsyncFileChannel.java │ │ ├── AsyncResult.java │ │ ├── AsyncServerSocketChannel.java │ │ ├── AsyncSocketChannel.java │ │ ├── BlockingIO.java │ │ ├── BlockingThreadSelector.java │ │ └── InterruptiblePromiseCustomizer.java │ │ ├── locks │ │ ├── AbstractAsyncLock.java │ │ ├── AsyncCountDownLatch.java │ │ ├── AsyncLock.java │ │ ├── AsyncSemaphore.java │ │ ├── AsyncSemaphoreBase.java │ │ ├── AsyncSemaphoreLock.java │ │ ├── DefaultAsyncLock.java │ │ ├── DefaultAsyncSemaphore.java │ │ └── DefaultAsyncSemaphoreLock.java │ │ └── var │ │ ├── ContextTrampoline.java │ │ ├── ContextVar.java │ │ ├── ContextVarGroup.java │ │ ├── ContextVarHelper.java │ │ ├── ContextualExecutor.java │ │ ├── ContextualExecutorService.java │ │ ├── ContextualPromiseCustomizer.java │ │ ├── ContextualScheduledExecutorService.java │ │ ├── ContextualTaskExecutorService.java │ │ ├── Contextualization.java │ │ ├── CustomModifiableContextVar.java │ │ ├── ModifiableContextVar.java │ │ ├── ModifiableContextVarGroup.java │ │ ├── ThreadLocalVar.java │ │ └── ThreadLocalVarGroup.java └── resources │ └── META-INF │ └── MANIFEST.MF └── test └── java └── net └── tascalate └── concurrent ├── DependentPromiseTests.java ├── EitherCompletableTaskTests.java ├── Exceptions.java ├── InterruptBlockingIO.java ├── InterruptibleAdHoc.java ├── J8Examples.java ├── LookupCancelMethod.java ├── OrTimeoutExceptionTests.java ├── PromisesTests.java ├── SimpleCompletableTaskTests.java ├── StopExecutorTests.java ├── TestMultitargetExceptionTraces.java ├── ThenComposeAsyncTest.java ├── TimeoutDifferentExecutors.java ├── WhenCompleteAsyncTest.java └── delays └── NanosTest.java /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | pom.xml.tag 3 | pom.xml.releaseBackup 4 | pom.xml.versionsBackup 5 | pom.xml.next 6 | release.properties 7 | dependency-reduced-pom.xml 8 | buildNumber.properties 9 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | net.tascalate.concurrent.lib 4 | Implementation of blocking (IO-Bound) cancellable java.util.concurrent.CompletionStage 5 | and related extensions to java.util.concurrent.ExecutorService-s. NO_M2ECLIPSE_SUPPORT: Project files created with the maven-eclipse-plugin are not supported in M2Eclipse. 6 | 7 | 8 | 9 | 10 | org.eclipse.jdt.core.javabuilder 11 | 12 | 13 | 14 | 15 | org.eclipse.m2e.core.maven2Builder 16 | 17 | 18 | 19 | 20 | 21 | org.eclipse.m2e.core.maven2Nature 22 | org.eclipse.jdt.core.javanature 23 | 24 | 25 | -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding//src/main/java=UTF-8 3 | encoding//src/main/resources=UTF-8 4 | encoding//src/test/java=UTF-8 5 | encoding//src/test/resources=UTF-8 6 | encoding/=UTF-8 7 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate 4 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=12 5 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 6 | org.eclipse.jdt.core.compiler.compliance=12 7 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 8 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 9 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 10 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 11 | org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled 12 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 13 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 14 | org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning 15 | org.eclipse.jdt.core.compiler.release=enabled 16 | org.eclipse.jdt.core.compiler.source=12 17 | -------------------------------------------------------------------------------- /.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /HEADER-DERRIVED.txt: -------------------------------------------------------------------------------- 1 | Original work: copyright 2009-2015 Lukáš Křečan 2 | (https://github.com/lukas-krecan/completion-stage/blob/completion-stage-0.0.9/src/main/java/net/javacrumbs/completionstage/CallbackRegistry.java) 3 | 4 | This class is based on the work create by Lukáš Křečan 5 | under the Apache License, Version 2.0. Please see 6 | https://github.com/lukas-krecan/completion-stage 7 | 8 | Modified work: copyright 2015-2025 Valery Silaev (http://vsilaev.com) 9 | 10 | Licensed under the Apache License, Version 2.0 (the "License"); 11 | you may not use this file except in compliance with the License. 12 | You may obtain a copy of the License at 13 | 14 | http://www.apache.org/licenses/LICENSE-2.0 15 | 16 | Unless required by applicable law or agreed to in writing, software 17 | distributed under the License is distributed on an "AS IS" BASIS, 18 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | See the License for the specific language governing permissions and 20 | limitations under the License. 21 | -------------------------------------------------------------------------------- /HEADER-ORIGINAL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2015-2025 Valery Silaev (http://vsilaev.com) 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /examples/net/tascalate/concurrent/ExecutorAwareThread.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent; 17 | 18 | import java.util.concurrent.Executor; 19 | import java.util.concurrent.ForkJoinTask; 20 | 21 | public class ExecutorAwareThread extends Thread { 22 | private final Executor executor; 23 | 24 | ExecutorAwareThread(Executor executor, ThreadGroup group, String name) { 25 | this(executor, group, null, name, 0); 26 | } 27 | 28 | protected ExecutorAwareThread(Executor executor, ThreadGroup group, Runnable target, String name) { 29 | this(executor, group, target, name, 0); 30 | } 31 | 32 | 33 | public ExecutorAwareThread(Executor executor, 34 | ThreadGroup group, 35 | Runnable target, 36 | String name, 37 | long stackSize) { 38 | super(group, target, name, stackSize); 39 | this.executor = executor; 40 | } 41 | 42 | public static Executor currentExecutor() { 43 | Thread t = Thread.currentThread(); 44 | if (t instanceof ExecutorAwareThread) { 45 | ExecutorAwareThread eat = (ExecutorAwareThread)t; 46 | return eat.executor; 47 | } else { 48 | return ForkJoinTask.getPool(); 49 | } 50 | } 51 | 52 | public static ThreadFactoryBuilder newThreadFactory(Executor executor) { 53 | return new ThreadFactoryBuilder() { 54 | @Override 55 | protected Thread createThread(ThreadGroup threadGroup, Runnable runnable, String name) { 56 | return new ExecutorAwareThread(executor, threadGroup, runnable, name); 57 | } 58 | }; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | module net.tascalate.concurrent { 17 | exports net.tascalate.concurrent; 18 | exports net.tascalate.concurrent.decorators; 19 | exports net.tascalate.concurrent.delays; 20 | exports net.tascalate.concurrent.io; 21 | exports net.tascalate.concurrent.locks; 22 | exports net.tascalate.concurrent.var; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/AsyncCloseable.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent; 17 | 18 | import java.util.concurrent.CompletionStage; 19 | import java.util.function.Function; 20 | 21 | /** 22 | * An object that may hold resources that must be explicitly released, and the release operation 23 | * should be performed asynchronously. 24 | * 25 | *

26 | * Examples of such resources are database connections, open file handles, socket descriptors 27 | * etc. While similar to {@link AutoCloseable}, this interface should be used when the resource 28 | * release operation may possibly be asynchronous. For example, if an object is thread-safe and 29 | * has many consumers, an implementation may require all current ongoing operations to complete 30 | * before resources are relinquished. 31 | * 32 | *

33 | * May be used with the methods {@link Promises#tryApplyEx(CompletionStage, Function)}, 34 | * {@link Promises#tryComposeEx(Promise, Function)} to emulate the behavior of a try 35 | * with resources block. 36 | * 37 | */ 38 | @FunctionalInterface 39 | public interface AsyncCloseable { 40 | /** 41 | * Release any resources associated with this object. 42 | * 43 | * @return a {@link CompletionStage} that completes when all resources associated with this object 44 | * have been released, or settled with an exception if the resources cannot be released. 45 | */ 46 | CompletionStage close(); 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/CompletableSubTask.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent; 17 | 18 | import java.util.concurrent.Callable; 19 | import java.util.concurrent.Executor; 20 | import java.util.concurrent.atomic.AtomicReference; 21 | 22 | /** 23 | * The {@link Promise} implementation for intermediate long-running blocking task 24 | * @author vsilaev 25 | * 26 | * @param 27 | */ 28 | class CompletableSubTask extends AbstractCompletableTask { 29 | 30 | private static class DelegatingCallable implements Callable { 31 | private final AtomicReference> delegateRef = new AtomicReference<>(); 32 | 33 | void setup(Callable delegate) { 34 | boolean updated = delegateRef.compareAndSet(null, delegate); 35 | if (!updated) { 36 | throw new IllegalStateException("Delegate may be set only once"); 37 | } 38 | } 39 | 40 | @Override 41 | public T call() throws Exception { 42 | Callable delegate = delegateRef.get(); 43 | if (null == delegate) { 44 | throw new IllegalStateException("Call is not configured"); 45 | } else { 46 | return delegate.call(); 47 | } 48 | } 49 | 50 | } 51 | 52 | private final DelegatingCallable action; 53 | 54 | CompletableSubTask(Executor executor) { 55 | this(executor, new DelegatingCallable<>()); 56 | } 57 | 58 | private CompletableSubTask(Executor executor, DelegatingCallable action) { 59 | super(executor, action); 60 | this.action = action; 61 | } 62 | 63 | @Override 64 | final void fireTransition(Callable code) { 65 | action.setup(code); 66 | task.run(); 67 | } 68 | 69 | @Override 70 | protected AbstractCompletableTask createCompletionStage(Executor executor) { 71 | return new CompletableSubTask(executor); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/CompletionStageRef.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent; 17 | 18 | import java.util.concurrent.CompletionStage; 19 | import java.util.concurrent.atomic.AtomicReference; 20 | import java.util.function.Function; 21 | 22 | class CompletionStageRef extends AtomicReference> { 23 | private static final long serialVersionUID = 1L; 24 | 25 | CompletionStage modify(CompletionStage newValue) { 26 | set(newValue); 27 | return newValue; 28 | } 29 | 30 | > Function captureResult(Function fn) { 31 | return v -> { 32 | F result = fn.apply(v); 33 | set(result); 34 | return result; 35 | }; 36 | } 37 | 38 | Runnable cancel = () -> { 39 | CompletionStage stage = get(); 40 | if (null != stage) { 41 | SharedFunctions.cancelPromise(stage, true); 42 | } 43 | }; 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/PaddedOutputStream.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent; 17 | 18 | import java.io.IOException; 19 | import java.io.OutputStream; 20 | import java.nio.charset.Charset; 21 | 22 | class PaddedOutputStream extends OutputStream { 23 | private final OutputStream delegate; 24 | private final byte[] padding; 25 | 26 | private volatile boolean needPadding = false; 27 | 28 | public PaddedOutputStream(OutputStream delegate, String padding) { 29 | this.delegate = delegate; 30 | this.padding = padding.getBytes(Charset.defaultCharset()); 31 | } 32 | 33 | private void writePaddingIfNecessary() throws IOException { 34 | if (needPadding) { 35 | delegate.write(padding); 36 | needPadding = false; 37 | } 38 | } 39 | 40 | @Override 41 | public void write(int b) throws IOException { 42 | if (b != 0xD && b!= 0xA) { 43 | writePaddingIfNecessary(); 44 | } 45 | if (b == 0xD || b == 0xA) { 46 | needPadding = true; 47 | } 48 | delegate.write(b); 49 | } 50 | 51 | @Override 52 | public void write(byte[] b, int off, int len) throws IOException { 53 | writePaddingIfNecessary(); 54 | int from = off; 55 | boolean needPadding = false; 56 | for (int idx = off; idx < len; idx++) { 57 | if (b[idx] == 0xD || b[idx] == 0xA) { 58 | needPadding = true; 59 | } else if (needPadding) { 60 | if (from < idx) { 61 | delegate.write(b, from, idx - from + 1); 62 | } 63 | delegate.write(padding); 64 | needPadding = false; 65 | from = idx + 1; 66 | } 67 | } 68 | if (from < len) { 69 | delegate.write(b, from, len); 70 | } 71 | this.needPadding = needPadding; 72 | } 73 | 74 | @Override 75 | public void flush() throws IOException { 76 | delegate.flush(); 77 | } 78 | 79 | @Override 80 | public void close() throws IOException { 81 | try (OutputStream stream = delegate) { 82 | flush(); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/PaddedWriter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent; 17 | 18 | import java.io.IOException; 19 | import java.io.Writer; 20 | 21 | class PaddedWriter extends Writer { 22 | private final Writer delegate; 23 | private final char[] padding; 24 | 25 | private volatile boolean needPadding = false; 26 | 27 | public PaddedWriter(Writer delegate, String padding) { 28 | this.delegate = delegate; 29 | this.padding = padding.toCharArray(); 30 | } 31 | 32 | private void writePaddingIfNecessary() throws IOException { 33 | if (needPadding) { 34 | delegate.write(padding); 35 | needPadding = false; 36 | } 37 | } 38 | 39 | @Override 40 | public void write(int b) throws IOException { 41 | if (b != 0xD && b!= 0xA) { 42 | writePaddingIfNecessary(); 43 | } 44 | if (b == 0xD || b == 0xA) { 45 | needPadding = true; 46 | } 47 | delegate.write(b); 48 | } 49 | 50 | @Override 51 | public void write(char[] b, int off, int len) throws IOException { 52 | writePaddingIfNecessary(); 53 | int from = off; 54 | boolean needPadding = false; 55 | for (int idx = off; idx < len; idx++) { 56 | if (b[idx] == 0xD || b[idx] == 0xA) { 57 | needPadding = true; 58 | } else if (needPadding) { 59 | if (from < idx) { 60 | delegate.write(b, from, idx - from + 1); 61 | } 62 | delegate.write(padding); 63 | needPadding = false; 64 | from = idx + 1; 65 | } 66 | } 67 | if (from < len) { 68 | delegate.write(b, from, len); 69 | } 70 | this.needPadding = needPadding; 71 | } 72 | 73 | @Override 74 | public void flush() throws IOException { 75 | delegate.flush(); 76 | } 77 | 78 | @Override 79 | public void close() throws IOException { 80 | try (Writer writer = delegate) { 81 | flush(); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/PromiseHelper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent; 17 | 18 | import java.util.concurrent.CompletionStage; 19 | import java.util.concurrent.Executor; 20 | import java.util.function.Function; 21 | 22 | class PromiseHelper { 23 | private PromiseHelper() {} 24 | 25 | abstract static class Either { 26 | A left() { throw new UnsupportedOperationException(); } 27 | B right() { throw new UnsupportedOperationException(); } 28 | 29 | abstract boolean isLeft(); 30 | 31 | static Either lift(A a, B b) { 32 | if (null == b) { 33 | return new Either() { 34 | A left() { return a; } 35 | boolean isLeft() { return true; } 36 | }; 37 | } else { 38 | return new Either() { 39 | B right() { return b; } 40 | boolean isLeft() { return false; } 41 | }; 42 | } 43 | } 44 | } 45 | 46 | static Promise exceptionallyAsync(Promise p, 47 | Function fn) { 48 | 49 | DependentPromise> h = p.dependent().handle(Either::lift, false); 50 | return 51 | h.thenCompose(e -> e.isLeft() ? p : h.thenApplyAsync(in -> fn.apply(in.right())), true) 52 | .unwrap(); 53 | } 54 | 55 | static Promise exceptionallyAsync(Promise p, 56 | Function fn, 57 | Executor executor) { 58 | DependentPromise> h = p.dependent().handle(Either::lift, false); 59 | return 60 | h.thenCompose(e -> e.isLeft() ? p : h.thenApplyAsync(in -> fn.apply(in.right()), executor), true) 61 | .unwrap(); 62 | } 63 | 64 | static Promise exceptionallyCompose(Promise p, 65 | Function> fn) { 66 | 67 | DependentPromise> h = p.dependent().handle(Either::lift, false); 68 | return 69 | h.thenCompose(e -> e.isLeft() ? p : fn.apply(e.right()), true) 70 | .unwrap(); 71 | } 72 | 73 | static Promise exceptionallyComposeAsync(Promise p, 74 | Function> fn) { 75 | 76 | DependentPromise> h = p.dependent().handle(Either::lift, false); 77 | return 78 | h.thenCompose(e -> e.isLeft() ? p : h.thenComposeAsync(in -> fn.apply(in.right())), true) 79 | .unwrap(); 80 | } 81 | 82 | static Promise exceptionallyComposeAsync(Promise p, 83 | Function> fn, 84 | Executor executor) { 85 | DependentPromise> h = p.dependent().handle(Either::lift, false); 86 | return 87 | h.thenCompose(e -> e.isLeft() ? p : h.thenComposeAsync(in -> fn.apply(in.right()), executor), true) 88 | .unwrap(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/PromiseOrigin.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent; 17 | 18 | import java.util.EnumSet; 19 | import java.util.Set; 20 | 21 | import static java.util.Collections.unmodifiableSet; 22 | 23 | public enum PromiseOrigin { 24 | THIS, PARAM; 25 | 26 | public static final Set ALL = unmodifiableSet(EnumSet.of(THIS, PARAM)); 27 | public static final Set NONE = unmodifiableSet(EnumSet.noneOf(PromiseOrigin.class)); 28 | public static final Set THIS_ONLY = unmodifiableSet(EnumSet.of(THIS)); 29 | public static final Set PARAM_ONLY = unmodifiableSet(EnumSet.of(PARAM)); 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/RetryCallable.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent; 17 | 18 | import java.util.concurrent.Callable; 19 | 20 | @FunctionalInterface 21 | public interface RetryCallable { 22 | V call(RetryContext ctx) throws Exception; 23 | 24 | public static RetryCallable from(Callable callable) { 25 | return ctx -> callable.call(); 26 | } 27 | 28 | public static RetryCallable from(RetryRunnable runnable) { 29 | return ctx -> { 30 | runnable.run(ctx); 31 | return null; 32 | }; 33 | } 34 | 35 | public static RetryCallable from(Runnable runnable) { 36 | return RetryCallable.from(RetryRunnable.from(runnable)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/RetryContext.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent; 17 | 18 | import java.time.Duration; 19 | 20 | public final class RetryContext { 21 | private final int retryCount; 22 | private final Duration lastCallDuration; 23 | private final T lastResult; 24 | private final Throwable lastError; 25 | 26 | private RetryContext(int retryCount, Duration lastCallDuration, T lastResult, Throwable lastError) { 27 | this.retryCount = retryCount; 28 | this.lastCallDuration = lastCallDuration; 29 | this.lastResult = lastResult; 30 | this.lastError = lastError; 31 | } 32 | 33 | public int getRetryCount() { 34 | return retryCount; 35 | } 36 | 37 | public Duration getLastCallDuration() { 38 | return lastCallDuration; 39 | } 40 | 41 | public T getLastResult() { 42 | return lastResult; 43 | } 44 | 45 | public Throwable getLastError() { 46 | return lastError; 47 | } 48 | 49 | public RetryContext overrideRetryCount(int newRetryCount) { 50 | return new RetryContext<>(newRetryCount, lastCallDuration, lastResult, lastError); 51 | } 52 | 53 | public RetryContext overrideLastCallDuration(Duration newDuration) { 54 | return new RetryContext<>(retryCount, newDuration, lastResult, lastError); 55 | } 56 | 57 | public RetryContext overrideLastResult(T newResult) { 58 | return new RetryContext<>(retryCount, lastCallDuration, newResult, lastError); 59 | } 60 | 61 | public RetryContext overrideLastError(Throwable newError) { 62 | return new RetryContext<>(retryCount, lastCallDuration, lastResult, newError); 63 | } 64 | 65 | static RetryContext initial() { 66 | return new RetryContext<>(0, Duration.ZERO, null, null); 67 | } 68 | 69 | RetryContext nextRetry(Duration callDuration, T lastResult) { 70 | return new RetryContext<>(retryCount + 1, callDuration, lastResult, null); 71 | } 72 | 73 | RetryContext nextRetry(Duration callDuration, Throwable lastError) { 74 | return new RetryContext<>(retryCount + 1, callDuration, null, lastError); 75 | } 76 | 77 | 78 | RetryException asFailure() { 79 | RetryException result = new RetryException(retryCount, lastCallDuration, lastError); 80 | result.fillInStackTrace(); 81 | return result; 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/RetryException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent; 17 | 18 | import java.time.Duration; 19 | 20 | public class RetryException extends Exception { 21 | private static final long serialVersionUID = 1L; 22 | 23 | private final int retry; 24 | private final Duration lastCallDuration; 25 | 26 | public RetryException() { 27 | retry = 0; 28 | lastCallDuration = Duration.ZERO; 29 | } 30 | 31 | public RetryException(int retry, Duration lastCallDuration, Throwable lastThrowable) { 32 | super(lastThrowable); 33 | this.retry = retry; 34 | this.lastCallDuration = lastCallDuration; 35 | } 36 | 37 | public int getRetryCount() { 38 | return retry; 39 | } 40 | 41 | public Duration getLastCallDuration() { 42 | return lastCallDuration; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/RetryRunnable.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent; 17 | 18 | @FunctionalInterface 19 | public interface RetryRunnable { 20 | void run(RetryContext ctx); 21 | 22 | public static RetryRunnable from(Runnable runnable) { 23 | return ctx -> runnable.run(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/RunnablePromise.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent; 17 | 18 | import java.util.concurrent.RunnableFuture; 19 | 20 | public interface RunnablePromise extends Promise, RunnableFuture { 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/TaskCompletionService.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent; 17 | 18 | import java.util.concurrent.Callable; 19 | import java.util.concurrent.CompletionService; 20 | import java.util.concurrent.TimeUnit; 21 | 22 | public interface TaskCompletionService extends CompletionService { 23 | 24 | Promise submit(Callable task); 25 | 26 | Promise submit(Runnable task, V result); 27 | 28 | Promise take() throws InterruptedException; 29 | 30 | Promise poll(); 31 | 32 | Promise poll(long timeout, TimeUnit unit) throws InterruptedException; 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/TaskExecutorService.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent; 17 | 18 | import java.util.concurrent.Callable; 19 | import java.util.concurrent.ExecutorService; 20 | 21 | /** 22 | * Specialization of {@link ExecutorService} that uses {@link Promise} as a result of submit(...) methods. 23 | * 24 | * @author vsilaev 25 | * 26 | */ 27 | public interface TaskExecutorService extends ExecutorService { 28 | 29 | Promise submit(Callable task); 30 | 31 | Promise submit(Runnable task, T result); 32 | 33 | Promise submit(Runnable task); 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/ThreadFactoryBuilder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent; 17 | 18 | 19 | public class ThreadFactoryBuilder extends AbstractThreadFactoryBuilder { 20 | protected ThreadFactoryBuilder() {} 21 | 22 | @Override 23 | protected Thread createThread(ThreadGroup threadGroup, Runnable runnable, String name) { 24 | return new Thread(threadGroup, runnable, name); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/ThreadGroupBuilder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent; 17 | 18 | public final class ThreadGroupBuilder { 19 | 20 | private String name; 21 | private Boolean isDaemon; 22 | private ThreadGroup parent; 23 | private Integer maxPriority; 24 | 25 | ThreadGroupBuilder() { 26 | } 27 | 28 | public ThreadGroupBuilder withName(String name) { 29 | this.name = name; 30 | return this; 31 | } 32 | 33 | public ThreadGroupBuilder withDaemonFlag(boolean isDaemon) { 34 | this.isDaemon = isDaemon; 35 | return this; 36 | } 37 | 38 | public ThreadGroupBuilder withDefaultDaemonFlag() { 39 | this.isDaemon = null; 40 | return this; 41 | } 42 | 43 | public ThreadGroupBuilder withMaxPriority(int maxPriority) { 44 | if (maxPriority < Thread.MIN_PRIORITY || maxPriority > Thread.MAX_PRIORITY ) { 45 | throw new IllegalArgumentException(String.format( 46 | "ThreadGroup max priority (%d) must be within [%d..%d]", 47 | maxPriority, Thread.MIN_PRIORITY, Thread.MAX_PRIORITY)); 48 | } 49 | this.maxPriority = maxPriority; 50 | return this; 51 | } 52 | 53 | public ThreadGroupBuilder withDefaultMaxPriority() { 54 | this.maxPriority = null; 55 | return this; 56 | } 57 | 58 | public ThreadGroupBuilder withParent(ThreadGroup parent) { 59 | this.parent = parent; 60 | return this; 61 | } 62 | 63 | public ThreadGroup build() { 64 | ThreadGroup g = new ThreadGroup(parent == null ? Thread.currentThread().getThreadGroup() : parent, name); 65 | if (null != isDaemon && g.isDaemon() != isDaemon) { 66 | g.setDaemon(isDaemon); 67 | } 68 | if (null != maxPriority && g.getMaxPriority() != maxPriority) { 69 | g.setMaxPriority(maxPriority); 70 | } 71 | return g; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/ThreadPoolTaskExecutor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent; 17 | 18 | import java.util.concurrent.BlockingQueue; 19 | import java.util.concurrent.Callable; 20 | import java.util.concurrent.Executors; 21 | import java.util.concurrent.RejectedExecutionHandler; 22 | import java.util.concurrent.RunnableFuture; 23 | import java.util.concurrent.ThreadFactory; 24 | import java.util.concurrent.ThreadPoolExecutor; 25 | import java.util.concurrent.TimeUnit; 26 | 27 | 28 | /** 29 | *

Concrete implementation of {@link TaskExecutorService} interface. 30 | *

Specialization of {@link ThreadPoolExecutor} that uses {@link Promise} as a result of submit(...) methods. 31 | * 32 | * @author vsilaev 33 | * 34 | */ 35 | public class ThreadPoolTaskExecutor extends ThreadPoolExecutor implements TaskExecutorService { 36 | 37 | public ThreadPoolTaskExecutor(int corePoolSize, int maximumPoolSize, 38 | long keepAliveTime, TimeUnit unit, 39 | BlockingQueue workQueue, 40 | RejectedExecutionHandler handler) { 41 | 42 | super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler); 43 | } 44 | 45 | public ThreadPoolTaskExecutor(int corePoolSize, int maximumPoolSize, 46 | long keepAliveTime, TimeUnit unit, 47 | BlockingQueue workQueue, 48 | ThreadFactory threadFactory, 49 | RejectedExecutionHandler handler) { 50 | 51 | super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler); 52 | } 53 | 54 | public ThreadPoolTaskExecutor(int corePoolSize, int maximumPoolSize, 55 | long keepAliveTime, TimeUnit unit, 56 | BlockingQueue workQueue, 57 | ThreadFactory threadFactory) { 58 | 59 | super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory); 60 | } 61 | 62 | public ThreadPoolTaskExecutor(int corePoolSize, int maximumPoolSize, 63 | long keepAliveTime, TimeUnit unit, 64 | BlockingQueue workQueue) { 65 | 66 | super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); 67 | } 68 | 69 | @Override 70 | public Promise submit(Runnable task) { 71 | return (Promise) super.submit(task); 72 | } 73 | 74 | @Override 75 | public Promise submit(Runnable task, T result) { 76 | return (Promise) super.submit(task, result); 77 | } 78 | 79 | @Override 80 | public Promise submit(Callable task) { 81 | return (Promise) super.submit(task); 82 | } 83 | 84 | @Override 85 | protected RunnableFuture newTaskFor(Runnable runnable, T value) { 86 | return newTaskFor(Executors.callable(runnable, value)); 87 | } 88 | 89 | @Override 90 | protected RunnableFuture newTaskFor(Callable callable) { 91 | return TaskExecutors.newRunnablePromise(this, callable); 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/Try.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent; 17 | 18 | import java.time.Duration; 19 | import java.util.concurrent.CancellationException; 20 | import java.util.concurrent.TimeoutException; 21 | import java.util.function.Supplier; 22 | 23 | abstract class Try { 24 | 25 | abstract R done(); 26 | abstract boolean isSuccess(); 27 | abstract boolean isCancel(); 28 | abstract Promise asPromise(); 29 | 30 | static final class Success extends Try { 31 | 32 | private final R result; 33 | 34 | Success(R result) { 35 | this.result = result; 36 | } 37 | 38 | @Override 39 | R done() { 40 | return result; 41 | } 42 | 43 | @Override 44 | Promise asPromise() { 45 | return Promises.success(result); 46 | } 47 | 48 | @Override 49 | boolean isSuccess() { 50 | return true; 51 | } 52 | 53 | @Override 54 | boolean isCancel() { 55 | return false; 56 | } 57 | } 58 | 59 | static final class Failure extends Try { 60 | 61 | private final Throwable error; 62 | 63 | Failure(Throwable error) { 64 | this.error = error; 65 | } 66 | 67 | @Override 68 | R done() { 69 | if (error instanceof Error) { 70 | throw (Error)error; 71 | } else if (error instanceof CancellationException) { 72 | throw (CancellationException)error; 73 | } else { 74 | throw SharedFunctions.wrapCompletionException(error); 75 | } 76 | } 77 | 78 | @Override 79 | Promise asPromise() { 80 | return Promises.failure(error); 81 | } 82 | 83 | @Override 84 | boolean isSuccess() { 85 | return false; 86 | } 87 | 88 | @Override 89 | boolean isCancel() { 90 | Throwable ex = SharedFunctions.unwrapCompletionException(error); 91 | return ex instanceof CancellationException; 92 | } 93 | 94 | } 95 | 96 | static Try success(R result) { 97 | return new Success(result); 98 | } 99 | 100 | static Try failure(Throwable error) { 101 | return new Failure(error); 102 | } 103 | 104 | static Try handle(R result, Throwable error, Promise timeout) { 105 | if (null != timeout) { 106 | timeout.cancel(true); 107 | } 108 | return null == error ? Try.success(result) : Try.failure(error); 109 | } 110 | 111 | static Try doneOrTimeout(Try result, Duration duration) { 112 | return null != result ? result : Try.failure(new TimeoutException("Timeout after " + duration)); 113 | } 114 | 115 | static Supplier> call(Supplier supplier) { 116 | return () -> { 117 | try { 118 | return Try.success(supplier.get()); 119 | } catch (Throwable ex) { 120 | return Try.failure(ex); 121 | } 122 | }; 123 | } 124 | 125 | @SuppressWarnings("unchecked") 126 | static Try nothing() { 127 | return (Try)NOTHING; 128 | } 129 | 130 | private static final Try NOTHING = success(null); 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/core/CompletionStageAPI.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.core; 17 | 18 | import java.util.concurrent.CompletableFuture; 19 | import java.util.concurrent.CompletionStage; 20 | import java.util.concurrent.Executor; 21 | import java.util.function.Function; 22 | 23 | public interface CompletionStageAPI { 24 | 25 | boolean defaultExecutorOverridable(); 26 | 27 | Executor defaultExecutorOf(CompletableFuture completableFuture); 28 | 29 | CompletionStage exceptionallyAsync(CompletionStage delegate, 30 | Function fn); 31 | CompletionStage exceptionallyAsync(CompletionStage delegate, 32 | Function fn, Executor executor); 33 | 34 | CompletionStage exceptionallyCompose(CompletionStage delegate, 35 | Function> fn); 36 | 37 | CompletionStage exceptionallyComposeAsync(CompletionStage delegate, 38 | Function> fn); 39 | 40 | CompletionStage exceptionallyComposeAsync(CompletionStage delegate, 41 | Function> fn, Executor executor); 42 | 43 | static CompletionStageAPI current() { 44 | return CurrentCompletionStageAPI.INSTANCE; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/core/CurrentCompletionStageAPI.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.core; 17 | 18 | final class CurrentCompletionStageAPI { 19 | static final CompletionStageAPI INSTANCE; 20 | 21 | private CurrentCompletionStageAPI() {} 22 | 23 | private static int getJavaVersion() { 24 | String version = System.getProperty("java.version"); 25 | 26 | if (version.startsWith("1.")) { 27 | version = version.substring(2, version.indexOf('.', 2)); 28 | } else { 29 | int dot = version.indexOf("."); 30 | if (dot > 0) { 31 | version = version.substring(0, dot); 32 | } 33 | } 34 | return Integer.parseInt(version); 35 | } 36 | 37 | static { 38 | int version = getJavaVersion(); 39 | if (version >= 12) 40 | INSTANCE = new J12CompletionStageAPI(); 41 | else if (version >= 9) 42 | INSTANCE = new J9CompletionStageAPI(); 43 | else 44 | INSTANCE = new J8CompletionStageAPI(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/core/Decorator.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.core; 17 | 18 | import java.util.concurrent.CompletionStage; 19 | 20 | public interface Decorator { 21 | CompletionStage α(); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/core/FunctionMemoization.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2024 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.core; 17 | 18 | import java.lang.ref.Reference; 19 | import java.lang.ref.ReferenceQueue; 20 | import java.util.concurrent.ConcurrentHashMap; 21 | import java.util.concurrent.ConcurrentMap; 22 | import java.util.function.Function; 23 | 24 | class FunctionMemoization implements Function { 25 | private final KeyedLocks producerMutexes = new KeyedLocks<>(); 26 | private final ConcurrentMap valueMap = new ConcurrentHashMap<>(); 27 | 28 | private final Function fn; 29 | private final ReferenceType keyRefType; 30 | private final ReferenceType valueRefType; 31 | private final ReferenceQueue queue; 32 | 33 | FunctionMemoization(Function fn) { 34 | this(ReferenceType.WEAK, ReferenceType.SOFT, fn); 35 | } 36 | 37 | FunctionMemoization(ReferenceType keyRefType, ReferenceType valueRefType, Function fn) { 38 | this.fn = fn; 39 | this.keyRefType = keyRefType; 40 | this.valueRefType = valueRefType; 41 | this.queue = keyRefType.createKeyReferenceQueue(); 42 | } 43 | 44 | @Override 45 | public V apply(K key) { 46 | expungeStaleEntries(); 47 | 48 | Object lookupKeyRef = keyRefType.createLookupKey(key); 49 | Object valueRef; 50 | 51 | // Try to get a cached value. 52 | valueRef = valueMap.get(lookupKeyRef); 53 | V value; 54 | 55 | if (valueRef != null) { 56 | value = valueRefType.dereference(valueRef); 57 | if (value != null) { 58 | // A cached value was found. 59 | return value; 60 | } 61 | } 62 | 63 | try (KeyedLocks.Lock lock = producerMutexes.acquire(key)) { 64 | // Double-check after getting mutex 65 | valueRef = valueMap.get(lookupKeyRef); 66 | value = valueRef == null ? null : valueRefType.dereference(valueRef); 67 | if (value == null) { 68 | value = fn.apply(key); 69 | valueMap.put( 70 | keyRefType.createKeyReference(key, queue), 71 | valueRefType.createValueReference(value) 72 | ); 73 | } 74 | } catch (InterruptedException ex) { 75 | throw new RuntimeException(ex); 76 | } 77 | return value; 78 | } 79 | 80 | public V forget(K key) { 81 | try (KeyedLocks.Lock lock = producerMutexes.acquire(key)) { 82 | Object valueRef = valueMap.remove(keyRefType.createLookupKey(key)); 83 | return valueRef == null ? null : valueRefType.dereference(valueRef); 84 | } catch (InterruptedException ex) { 85 | throw new RuntimeException(ex); 86 | } 87 | } 88 | 89 | private void expungeStaleEntries() { 90 | if (null == queue) { 91 | return; 92 | } 93 | for (Reference ref; (ref = queue.poll()) != null;) { 94 | @SuppressWarnings("unchecked") 95 | Reference keyRef = (Reference) ref; 96 | // keyRef now is equal only to itself while referent is cleared already 97 | // so it's safe to remove it without ceremony (like getOrCreateMutex(keyRef) usage) 98 | valueMap.remove(keyRef); 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/core/J12CompletionStageAPI.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.core; 17 | 18 | import java.util.concurrent.CompletableFuture; 19 | import java.util.concurrent.CompletionStage; 20 | import java.util.concurrent.Executor; 21 | import java.util.function.Function; 22 | 23 | class J12CompletionStageAPI implements CompletionStageAPI { 24 | 25 | J12CompletionStageAPI() {} 26 | 27 | @Override 28 | public boolean defaultExecutorOverridable() { 29 | return true; 30 | } 31 | 32 | @Override 33 | public Executor defaultExecutorOf(CompletableFuture completableFuture) { 34 | return completableFuture.defaultExecutor(); 35 | } 36 | 37 | @Override 38 | public CompletionStage exceptionallyAsync(CompletionStage delegate, 39 | Function fn) { 40 | return delegate.exceptionallyAsync(fn); 41 | } 42 | 43 | @Override 44 | public CompletionStage exceptionallyAsync(CompletionStage delegate, 45 | Function fn, Executor executor) { 46 | return delegate.exceptionallyAsync(fn, executor); 47 | } 48 | 49 | @Override 50 | public CompletionStage exceptionallyCompose(CompletionStage delegate, 51 | Function> fn) { 52 | return delegate.exceptionallyCompose(fn); 53 | } 54 | 55 | @Override 56 | public CompletionStage exceptionallyComposeAsync(CompletionStage delegate, 57 | Function> fn) { 58 | return delegate.exceptionallyComposeAsync(fn); 59 | } 60 | 61 | @Override 62 | public CompletionStage exceptionallyComposeAsync(CompletionStage delegate, 63 | Function> fn, Executor executor) { 64 | return delegate.exceptionallyComposeAsync(fn, executor); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/core/J8CompletionStageAPI.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.core; 17 | 18 | import java.util.concurrent.CompletableFuture; 19 | import java.util.concurrent.CompletionStage; 20 | import java.util.concurrent.Executor; 21 | import java.util.concurrent.ForkJoinPool; 22 | import java.util.function.Function; 23 | 24 | class J8CompletionStageAPI implements CompletionStageAPI { 25 | 26 | J8CompletionStageAPI() {} 27 | 28 | @Override 29 | public boolean defaultExecutorOverridable() { 30 | return false; 31 | } 32 | 33 | @Override 34 | public Executor defaultExecutorOf(CompletableFuture completableFuture) { 35 | return ForkJoinPool.commonPool(); 36 | } 37 | 38 | @Override 39 | public CompletionStage exceptionallyAsync(CompletionStage delegate, 40 | Function fn) { 41 | return delegate.handle((r, ex) -> ex == null ? 42 | delegate : 43 | delegate.handleAsync((r1, ex1) -> fn.apply(ex1))) 44 | .thenCompose(Function.identity()); 45 | } 46 | 47 | @Override 48 | public CompletionStage exceptionallyAsync(CompletionStage delegate, 49 | Function fn, Executor executor) { 50 | return delegate.handle((r, ex) -> ex == null ? 51 | delegate : 52 | delegate.handleAsync((r1, ex1) -> fn.apply(ex1), executor)) 53 | .thenCompose(Function.identity()); 54 | } 55 | 56 | @Override 57 | public CompletionStage exceptionallyCompose(CompletionStage delegate, 58 | Function> fn) { 59 | return delegate.handle((r, ex) -> ex == null ? delegate : fn.apply(ex)) 60 | .thenCompose(Function.identity()); 61 | } 62 | 63 | @Override 64 | public CompletionStage exceptionallyComposeAsync(CompletionStage delegate, 65 | Function> fn) { 66 | return delegate.handle((r, ex) -> ex == null ? 67 | delegate : 68 | delegate.handleAsync((r1, ex1) -> fn.apply(ex1)) 69 | .thenCompose(Function.identity())) 70 | .thenCompose(Function.identity()); 71 | } 72 | 73 | @Override 74 | public CompletionStage exceptionallyComposeAsync(CompletionStage delegate, 75 | Function> fn, Executor executor) { 76 | return delegate.handle((r, ex) -> ex == null ? 77 | delegate : 78 | delegate.handleAsync((r1, ex1) -> fn.apply(ex1), executor) 79 | .thenCompose(Function.identity())) 80 | .thenCompose(Function.identity()); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/core/J9CompletionStageAPI.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.core; 17 | 18 | import java.util.concurrent.CompletableFuture; 19 | import java.util.concurrent.Executor; 20 | 21 | class J9CompletionStageAPI extends J8CompletionStageAPI { 22 | 23 | J9CompletionStageAPI() {} 24 | 25 | @Override 26 | public boolean defaultExecutorOverridable() { 27 | return true; 28 | } 29 | 30 | @Override 31 | public Executor defaultExecutorOf(CompletableFuture completableFuture) { 32 | return completableFuture.defaultExecutor(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/decorators/AbstractFutureDecorator.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.decorators; 17 | 18 | import java.util.concurrent.CompletableFuture; 19 | import java.util.concurrent.CompletionStage; 20 | import java.util.concurrent.ExecutionException; 21 | import java.util.concurrent.Future; 22 | import java.util.concurrent.TimeUnit; 23 | import java.util.concurrent.TimeoutException; 24 | 25 | import net.tascalate.concurrent.Promise; 26 | 27 | /** 28 | * Helper class to create a concrete {@link Promise} subclass via delegation 29 | * to the wrapped {@link Promise}-like interface that implements both 30 | * {@link CompletionStage} and {@link Future} (including {@link CompletableFuture}) 31 | * 32 | * @author vsilaev 33 | * 34 | * @param 35 | * a type of the successfully resolved promise value 36 | * @param 37 | * a type of the concrete {@link CompletionStage} + {@link Future} subclass 38 | */ 39 | abstract public class AbstractFutureDecorator & Future> 40 | extends AbstractPromiseLikeDecorator 41 | implements CompletionStage, Future { 42 | 43 | protected AbstractFutureDecorator(D delegate) { 44 | super(delegate); 45 | } 46 | 47 | @Override 48 | public boolean cancel(boolean mayInterruptIfRunning) { 49 | return delegate.cancel(mayInterruptIfRunning); 50 | } 51 | 52 | @Override 53 | public boolean isCancelled() { 54 | return delegate.isCancelled(); 55 | } 56 | 57 | @Override 58 | public boolean isDone() { 59 | return delegate.isDone(); 60 | } 61 | 62 | @Override 63 | public T get() throws InterruptedException, ExecutionException { 64 | return delegate.get(); 65 | } 66 | 67 | @Override 68 | public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { 69 | return delegate.get(timeout, unit); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/decorators/BlockingCompletionStageDecorator.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.decorators; 17 | 18 | import java.util.concurrent.CompletionStage; 19 | import java.util.concurrent.Future; 20 | 21 | import net.tascalate.concurrent.Promise; 22 | 23 | public class BlockingCompletionStageDecorator & Future> 24 | extends AbstractFutureDecorator 25 | implements Promise { 26 | 27 | protected BlockingCompletionStageDecorator(D delegate) { 28 | super(delegate); 29 | } 30 | 31 | @Override 32 | protected Promise wrapNew(CompletionStage original) { 33 | return new BlockingCompletionStageDecorator<>(cast(original)); 34 | } 35 | 36 | private static & Future> D cast(CompletionStage stage) { 37 | if (!(stage instanceof Future)) { 38 | throw new IllegalArgumentException( 39 | "Parameter must implement both " + 40 | CompletionStage.class.getName() + " and " + 41 | Future.class.getName() 42 | ); 43 | } 44 | @SuppressWarnings("unchecked") 45 | D result = (D)stage; 46 | return result; 47 | } 48 | 49 | public static Promise from(CompletionStage stage) { 50 | return new BlockingCompletionStageDecorator<>(cast(stage)); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/decorators/CompletableFutureDecorator.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.decorators; 17 | 18 | import java.util.concurrent.CancellationException; 19 | import java.util.concurrent.CompletableFuture; 20 | import java.util.concurrent.CompletionException; 21 | import java.util.concurrent.CompletionStage; 22 | 23 | import net.tascalate.concurrent.Promise; 24 | 25 | public class CompletableFutureDecorator 26 | extends AbstractFutureDecorator> 27 | implements Promise { 28 | 29 | protected CompletableFutureDecorator() { 30 | this(new CompletableFuture()); 31 | } 32 | 33 | protected CompletableFutureDecorator(CompletableFuture delegate) { 34 | super(delegate); 35 | } 36 | 37 | @Override 38 | protected Promise wrapNew(CompletionStage original) { 39 | return new CompletableFutureDecorator<>((CompletableFuture)original); 40 | } 41 | 42 | @Override 43 | public T getNow(T valueIfAbsent) { 44 | return delegate.getNow(valueIfAbsent); 45 | } 46 | 47 | @Override 48 | public T join() throws CancellationException, CompletionException { 49 | return delegate.join(); 50 | } 51 | 52 | @Override 53 | public boolean isCompletedExceptionally() { 54 | return delegate.isCompletedExceptionally(); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/decorators/CompletionStageDecorator.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.decorators; 17 | 18 | import java.util.concurrent.CompletionStage; 19 | 20 | public class CompletionStageDecorator extends AbstractCompletionStageDecorator> { 21 | 22 | public CompletionStageDecorator(CompletionStage delegate) { 23 | super(delegate); 24 | } 25 | 26 | @Override 27 | protected CompletionStage wrapNew(CompletionStage original) { 28 | return new CompletionStageDecorator<>(original); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/decorators/CustomizableDependentPromiseDecorator.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.decorators; 17 | 18 | import java.util.concurrent.CompletionStage; 19 | import java.util.concurrent.Executor; 20 | import java.util.function.BiConsumer; 21 | import java.util.function.BiFunction; 22 | import java.util.function.Consumer; 23 | import java.util.function.Function; 24 | import java.util.function.Predicate; 25 | import java.util.function.Supplier; 26 | 27 | import net.tascalate.concurrent.DependentPromise; 28 | 29 | public class CustomizableDependentPromiseDecorator extends ExtendedDependentPromiseDecorator { 30 | 31 | private final PromiseCustomizer customizer; 32 | 33 | public CustomizableDependentPromiseDecorator(DependentPromise delegate, PromiseCustomizer customizer) { 34 | super(delegate); 35 | this.customizer = customizer; 36 | } 37 | 38 | @Override 39 | protected Runnable wrapArgument(Runnable original, boolean async) { 40 | return customizer.wrapArgument(original, async); 41 | } 42 | 43 | @Override 44 | protected Function wrapArgument(Function original, boolean async, boolean isCompose) { 45 | return customizer.wrapArgument(original, async, isCompose); 46 | } 47 | 48 | @Override 49 | protected Consumer wrapArgument(Consumer original, boolean async) { 50 | return customizer.wrapArgument(original, async); 51 | } 52 | 53 | @Override 54 | protected Supplier wrapArgument(Supplier original, boolean async) { 55 | return customizer.wrapArgument(original, async); 56 | } 57 | 58 | @Override 59 | protected Predicate wrapArgument(Predicate original, boolean async) { 60 | return customizer.wrapArgument(original, async); 61 | } 62 | 63 | @Override 64 | protected BiFunction wrapArgument(BiFunction original, boolean async) { 65 | return customizer.wrapArgument(original, async); 66 | } 67 | 68 | @Override 69 | protected BiConsumer wrapArgument(BiConsumer original, boolean async) { 70 | return customizer.wrapArgument(original, async); 71 | } 72 | 73 | @Override 74 | protected CompletionStage wrapArgument(CompletionStage original, boolean async) { 75 | return customizer.wrapArgument(original, async); 76 | } 77 | 78 | @Override 79 | protected Executor wrapArgument(Executor original) { 80 | return customizer.wrapArgument(original); 81 | } 82 | 83 | @Override 84 | protected DependentPromise wrapResult(CompletionStage original) { 85 | return new CustomizableDependentPromiseDecorator<>((DependentPromise)original, customizer); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/decorators/CustomizablePromiseDecorator.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.decorators; 17 | 18 | import java.util.Set; 19 | import java.util.concurrent.CompletionStage; 20 | import java.util.concurrent.Executor; 21 | import java.util.function.BiConsumer; 22 | import java.util.function.BiFunction; 23 | import java.util.function.Consumer; 24 | import java.util.function.Function; 25 | import java.util.function.Supplier; 26 | 27 | import net.tascalate.concurrent.DependentPromise; 28 | import net.tascalate.concurrent.Promise; 29 | import net.tascalate.concurrent.PromiseOrigin; 30 | 31 | public class CustomizablePromiseDecorator extends ExtendedPromiseDecorator { 32 | 33 | private final PromiseCustomizer customizer; 34 | 35 | public CustomizablePromiseDecorator(Promise delegate, PromiseCustomizer customizer) { 36 | super(delegate); 37 | this.customizer = customizer; 38 | } 39 | 40 | @Override 41 | protected Runnable wrapArgument(Runnable original, boolean async) { 42 | return customizer.wrapArgument(original, async); 43 | } 44 | 45 | @Override 46 | protected Function wrapArgument(Function original, boolean async, boolean isCompose) { 47 | return customizer.wrapArgument(original, async, isCompose); 48 | } 49 | 50 | @Override 51 | protected Consumer wrapArgument(Consumer original, boolean async) { 52 | return customizer.wrapArgument(original, async); 53 | } 54 | 55 | @Override 56 | protected Supplier wrapArgument(Supplier original, boolean async) { 57 | return customizer.wrapArgument(original, async); 58 | } 59 | 60 | @Override 61 | protected BiFunction wrapArgument(BiFunction original, boolean async) { 62 | return customizer.wrapArgument(original, async); 63 | } 64 | 65 | @Override 66 | protected BiConsumer wrapArgument(BiConsumer original, boolean async) { 67 | return customizer.wrapArgument(original, async); 68 | } 69 | 70 | @Override 71 | protected CompletionStage wrapArgument(CompletionStage original, boolean async) { 72 | return customizer.wrapArgument(original, async); 73 | } 74 | 75 | @Override 76 | protected Executor wrapArgument(Executor original) { 77 | return customizer.wrapArgument(original); 78 | } 79 | 80 | @Override 81 | protected Promise wrapResult(CompletionStage original) { 82 | return new CustomizablePromiseDecorator<>((Promise)original, customizer); 83 | } 84 | 85 | @Override 86 | public DependentPromise dependent() { 87 | return new CustomizableDependentPromiseDecorator<>( 88 | delegate.dependent(), customizer 89 | ); 90 | } 91 | 92 | @Override 93 | public DependentPromise dependent(Set defaultEnlistOptions) { 94 | return new CustomizableDependentPromiseDecorator<>( 95 | delegate.dependent(defaultEnlistOptions), customizer 96 | ); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/decorators/ExecutorBoundCompletionStage.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.decorators; 17 | 18 | import java.util.concurrent.CompletionStage; 19 | import java.util.concurrent.Executor; 20 | import java.util.function.BiConsumer; 21 | import java.util.function.BiFunction; 22 | import java.util.function.Consumer; 23 | import java.util.function.Function; 24 | 25 | class ExecutorBoundCompletionStage extends CompletionStageDecorator { 26 | private final Executor defaultExecutor; 27 | 28 | ExecutorBoundCompletionStage(CompletionStage delegate, Executor defaultExecutor) { 29 | super(delegate); 30 | this.defaultExecutor = defaultExecutor; 31 | } 32 | 33 | @Override 34 | protected CompletionStage wrapNew(CompletionStage original) { 35 | return new ExecutorBoundCompletionStage<>(original, defaultExecutor); 36 | } 37 | 38 | @Override 39 | public CompletionStage thenApplyAsync(Function fn) { 40 | return thenApplyAsync(fn, defaultExecutor); 41 | } 42 | 43 | @Override 44 | public CompletionStage thenAcceptAsync(Consumer action) { 45 | return thenAcceptAsync(action, defaultExecutor); 46 | } 47 | 48 | @Override 49 | public CompletionStage thenRunAsync(Runnable action) { 50 | return thenRunAsync(action, defaultExecutor); 51 | } 52 | 53 | @Override 54 | public CompletionStage thenCombineAsync(CompletionStage other, 55 | BiFunction fn) { 56 | return thenCombineAsync(other, fn, defaultExecutor); 57 | } 58 | 59 | @Override 60 | public CompletionStage thenAcceptBothAsync(CompletionStage other, 61 | BiConsumer action) { 62 | return thenAcceptBothAsync(other, action, defaultExecutor); 63 | } 64 | 65 | @Override 66 | public CompletionStage runAfterBothAsync(CompletionStage other, Runnable action) { 67 | return runAfterBothAsync(other, action, defaultExecutor); 68 | } 69 | 70 | @Override 71 | public CompletionStage applyToEitherAsync(CompletionStage other, Function fn) { 72 | return applyToEitherAsync(other, fn, defaultExecutor); 73 | } 74 | 75 | @Override 76 | public CompletionStage acceptEitherAsync(CompletionStage other, Consumer action) { 77 | return acceptEitherAsync(other, action, defaultExecutor); 78 | } 79 | 80 | @Override 81 | public CompletionStage runAfterEitherAsync(CompletionStage other, Runnable action) { 82 | return runAfterEitherAsync(other, action, defaultExecutor); 83 | } 84 | 85 | @Override 86 | public CompletionStage thenComposeAsync(Function> fn) { 87 | return thenComposeAsync(fn, defaultExecutor); 88 | } 89 | 90 | @Override 91 | public CompletionStage exceptionallyAsync(Function fn) { 92 | return exceptionallyAsync(fn, defaultExecutor); 93 | } 94 | 95 | @Override 96 | public CompletionStage exceptionallyComposeAsync(Function> fn) { 97 | return exceptionallyComposeAsync(fn, defaultExecutor); 98 | } 99 | 100 | @Override 101 | public CompletionStage whenCompleteAsync(BiConsumer action) { 102 | return whenCompleteAsync(action, defaultExecutor); 103 | } 104 | 105 | @Override 106 | public CompletionStage handleAsync(BiFunction fn) { 107 | return handleAsync(fn, defaultExecutor); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/decorators/PromiseCustomizer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.decorators; 17 | 18 | import java.util.concurrent.CompletionStage; 19 | import java.util.concurrent.Executor; 20 | import java.util.function.BiConsumer; 21 | import java.util.function.BiFunction; 22 | import java.util.function.Consumer; 23 | import java.util.function.Function; 24 | import java.util.function.Predicate; 25 | import java.util.function.Supplier; 26 | 27 | public interface PromiseCustomizer { 28 | 29 | default Runnable wrapArgument(Runnable original, boolean async) { 30 | return original; 31 | } 32 | 33 | default Function wrapArgument(Function original, boolean async, boolean isCompose) { 34 | return original; 35 | } 36 | 37 | default Consumer wrapArgument(Consumer original, boolean async) { 38 | return original; 39 | } 40 | 41 | default Supplier wrapArgument(Supplier original, boolean async) { 42 | return original; 43 | } 44 | 45 | default Predicate wrapArgument(Predicate original, boolean async) { 46 | return original; 47 | } 48 | 49 | default BiFunction wrapArgument(BiFunction original, boolean async) { 50 | return original; 51 | } 52 | 53 | default BiConsumer wrapArgument(BiConsumer original, boolean async) { 54 | return original; 55 | } 56 | 57 | default CompletionStage wrapArgument(CompletionStage original, boolean async) { 58 | return original; 59 | } 60 | 61 | default Executor wrapArgument(Executor original) { 62 | return original; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/delays/BoundedMaxDelayPolicy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Original work: copyright 2013 Tomasz Nurkiewicz 3 | * https://github.com/nurkiewicz/async-retry 4 | * 5 | * This class is based on the work create by Tomasz Nurkiewicz 6 | * under the Apache License, Version 2.0. Please see 7 | * https://github.com/nurkiewicz/async-retry/blob/master/src/main/java/com/nurkiewicz/asyncretry/backoff/BoundedMaxBackoff.java 8 | * 9 | * Modified work: copyright 2015-2021 Valery Silaev (http://vsilaev.com) 10 | * 11 | * Licensed under the Apache License, Version 2.0 (the "License"); 12 | * you may not use this file except in compliance with the License. 13 | * You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, software 18 | * distributed under the License is distributed on an "AS IS" BASIS, 19 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | * See the License for the specific language governing permissions and 21 | * limitations under the License. 22 | */ 23 | package net.tascalate.concurrent.delays; 24 | 25 | import java.time.Duration; 26 | 27 | import net.tascalate.concurrent.DelayPolicy; 28 | import net.tascalate.concurrent.RetryContext; 29 | 30 | public class BoundedMaxDelayPolicy extends DelayPolicyWrapper { 31 | 32 | public static final long DEFAULT_MAX_DELAY_MILLIS = 10_000; 33 | 34 | private final Duration maxDelay; 35 | 36 | public BoundedMaxDelayPolicy(DelayPolicy target) { 37 | this(target, DEFAULT_MAX_DELAY_MILLIS); 38 | } 39 | 40 | public BoundedMaxDelayPolicy(DelayPolicy target, long maxDelayMillis) { 41 | this(target, Duration.ofMillis(maxDelayMillis)); 42 | } 43 | 44 | public BoundedMaxDelayPolicy(DelayPolicy target, Duration maxDelay) { 45 | super(target); 46 | if (!DelayPolicy.isValid(maxDelay)) { 47 | throw new IllegalArgumentException("MaxDelay must be positive but was: " + maxDelay); 48 | } 49 | this.maxDelay = maxDelay; 50 | } 51 | 52 | @Override 53 | public Duration delay(RetryContext context) { 54 | return min(target.delay(context), maxDelay); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/delays/BoundedMinDelayPolicy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Original work: copyright 2013 Tomasz Nurkiewicz 3 | * https://github.com/nurkiewicz/async-retry 4 | * 5 | * This class is based on the work create by Tomasz Nurkiewicz 6 | * under the Apache License, Version 2.0. Please see 7 | * https://github.com/nurkiewicz/async-retry/blob/master/src/main/java/com/nurkiewicz/asyncretry/backoff/BoundedMinBackoff.java 8 | * 9 | * Modified work: copyright 2015-2021 Valery Silaev (http://vsilaev.com) 10 | * 11 | * Licensed under the Apache License, Version 2.0 (the "License"); 12 | * you may not use this file except in compliance with the License. 13 | * You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, software 18 | * distributed under the License is distributed on an "AS IS" BASIS, 19 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | * See the License for the specific language governing permissions and 21 | * limitations under the License. 22 | */ 23 | package net.tascalate.concurrent.delays; 24 | 25 | import java.time.Duration; 26 | 27 | import net.tascalate.concurrent.DelayPolicy; 28 | import net.tascalate.concurrent.RetryContext; 29 | 30 | public class BoundedMinDelayPolicy extends DelayPolicyWrapper { 31 | public static final long DEFAULT_MIN_DELAY_MILLIS = 100; 32 | 33 | private final Duration minDelay; 34 | 35 | public BoundedMinDelayPolicy(DelayPolicy target) { 36 | this(target, DEFAULT_MIN_DELAY_MILLIS); 37 | } 38 | 39 | public BoundedMinDelayPolicy(DelayPolicy target, long minDelayMillis) { 40 | this(target, Duration.ofMillis(minDelayMillis)); 41 | } 42 | 43 | public BoundedMinDelayPolicy(DelayPolicy target, Duration minDelay) { 44 | super(target); 45 | if (!DelayPolicy.isValid(minDelay)) { 46 | throw new IllegalArgumentException("MinDelay must be positive but was: " + minDelay); 47 | } 48 | this.minDelay = minDelay; 49 | } 50 | 51 | @Override 52 | public Duration delay(RetryContext context) { 53 | return max(target.delay(context), minDelay); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/delays/DelayPolicyWrapper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Original work: copyright 2013 Tomasz Nurkiewicz 3 | * https://github.com/nurkiewicz/async-retry 4 | * 5 | * This class is based on the work create by Tomasz Nurkiewicz 6 | * under the Apache License, Version 2.0. Please see 7 | * https://github.com/nurkiewicz/async-retry/blob/master/src/main/java/com/nurkiewicz/asyncretry/backoff/BackoffWrapper.java 8 | * 9 | * Modified work: copyright 2015-2021 Valery Silaev (http://vsilaev.com) 10 | * 11 | * Licensed under the Apache License, Version 2.0 (the "License"); 12 | * you may not use this file except in compliance with the License. 13 | * You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, software 18 | * distributed under the License is distributed on an "AS IS" BASIS, 19 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | * See the License for the specific language governing permissions and 21 | * limitations under the License. 22 | */ 23 | package net.tascalate.concurrent.delays; 24 | 25 | import java.time.Duration; 26 | import java.util.Objects; 27 | 28 | import net.tascalate.concurrent.DelayPolicy; 29 | 30 | public abstract class DelayPolicyWrapper implements DelayPolicy { 31 | 32 | protected final DelayPolicy target; 33 | 34 | public DelayPolicyWrapper(DelayPolicy target) { 35 | this.target = Objects.requireNonNull(target); 36 | } 37 | 38 | 39 | protected static Duration max(Duration a, Duration b) { 40 | return a.compareTo(b) > 0 ? a : b; 41 | } 42 | 43 | protected static Duration min(Duration a, Duration b) { 44 | return a.compareTo(b) < 0 ? a : b; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/delays/DurationCalcs.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.delays; 17 | 18 | import java.time.Duration; 19 | import java.time.temporal.ChronoUnit; 20 | import java.util.function.LongBinaryOperator; 21 | 22 | class DurationCalcs { 23 | private DurationCalcs() {} 24 | 25 | static Duration safeTransform(Duration duration, LongBinaryOperator isConversionSafe, LongBinaryOperator conversion) { 26 | long amount; 27 | int dimIdx; 28 | // Try to get value with best precision without throwing ArythmeticException due to overflow 29 | if (duration.compareTo(MAX_BY_NANOS) < 0) { 30 | amount = duration.toNanos(); 31 | dimIdx = 0; 32 | } else if (duration.compareTo(MAX_BY_MILLIS) < 0) { 33 | amount = duration.toMillis(); 34 | dimIdx = 2; 35 | } else { 36 | amount = duration.getSeconds(); 37 | dimIdx = 3; 38 | } 39 | int count = TIME_DIMENSIONS.length; 40 | for (; dimIdx < count; dimIdx++) { 41 | if (toBoolean(isConversionSafe.applyAsLong(amount, dimIdx))) { 42 | amount = conversion.applyAsLong(amount, dimIdx); 43 | return Duration.of(amount, TIME_DIMENSIONS[dimIdx]); 44 | } else { 45 | amount /= 1000; 46 | // try again on next iteration 47 | } 48 | } 49 | // return max possible value if doesn't fit 50 | return MAX_DURATION; 51 | 52 | } 53 | 54 | static long safeExtractAmount(Duration duration, int targetDimIdx) { 55 | long amount; 56 | int sourceDimIdx; 57 | if (duration.compareTo(MAX_BY_NANOS) < 0) { 58 | amount = duration.toNanos(); 59 | sourceDimIdx = 0; 60 | } else if (duration.compareTo(MAX_BY_MILLIS) < 0) { 61 | amount = duration.toMillis(); 62 | sourceDimIdx = 2; 63 | } else { 64 | amount = duration.getSeconds(); 65 | sourceDimIdx = 3; 66 | } 67 | // No conversion necessary 68 | if (sourceDimIdx == targetDimIdx) { 69 | return amount; 70 | } 71 | double factor = Math.pow(1000, sourceDimIdx - targetDimIdx); 72 | if ((double)Long.MAX_VALUE / amount > factor) { 73 | return (long)(amount * factor); 74 | } else { 75 | return Long.MAX_VALUE; 76 | } 77 | } 78 | 79 | static long toBoolean(boolean v) { 80 | return v ? 1 : 0; 81 | } 82 | 83 | private static boolean toBoolean(long v) { 84 | return v != 0; 85 | } 86 | 87 | private static final ChronoUnit[] TIME_DIMENSIONS = new ChronoUnit[]{ 88 | ChronoUnit.NANOS, ChronoUnit.MICROS, ChronoUnit.MILLIS, ChronoUnit.SECONDS 89 | }; 90 | 91 | private static final Duration MAX_BY_NANOS = Duration.ofNanos(Long.MAX_VALUE); 92 | private static final Duration MAX_BY_MILLIS = Duration.ofMillis(Long.MAX_VALUE); 93 | private static final Duration MAX_DURATION = Duration.ofSeconds(Long.MAX_VALUE).withNanos(999_999_999); 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/delays/ExponentialDelayPolicy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Original work: copyright 2013 Tomasz Nurkiewicz 3 | * https://github.com/nurkiewicz/async-retry 4 | * 5 | * This class is based on the work create by Tomasz Nurkiewicz 6 | * under the Apache License, Version 2.0. Please see 7 | * https://github.com/nurkiewicz/async-retry/blob/master/src/main/java/com/nurkiewicz/asyncretry/backoff/ExponentialDelayBackoff.java 8 | * 9 | * Modified work: copyright 2015-2021 Valery Silaev (http://vsilaev.com) 10 | * 11 | * Licensed under the Apache License, Version 2.0 (the "License"); 12 | * you may not use this file except in compliance with the License. 13 | * You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, software 18 | * distributed under the License is distributed on an "AS IS" BASIS, 19 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | * See the License for the specific language governing permissions and 21 | * limitations under the License. 22 | */ 23 | package net.tascalate.concurrent.delays; 24 | 25 | import java.time.Duration; 26 | 27 | import net.tascalate.concurrent.DelayPolicy; 28 | import net.tascalate.concurrent.RetryContext; 29 | 30 | public class ExponentialDelayPolicy implements DelayPolicy { 31 | private final Duration initialDelay; 32 | private final double multiplier; 33 | 34 | public ExponentialDelayPolicy(double multiplier) { 35 | this(FixedIntervalDelayPolicy.DEFAULT_PERIOD_MILLIS, multiplier); 36 | } 37 | 38 | public ExponentialDelayPolicy(long initialDelayMillis, double multiplier) { 39 | this(Duration.ofMillis(initialDelayMillis), multiplier); 40 | } 41 | 42 | public ExponentialDelayPolicy(Duration initialDelay, double multiplier) { 43 | if (!DelayPolicy.isValid(initialDelay)) { 44 | throw new IllegalArgumentException("Initial delay must be positive but was: " + initialDelay); 45 | } 46 | if (multiplier <= 0) { 47 | throw new IllegalArgumentException("Multiplier must be a positive number but was: " + multiplier); 48 | } 49 | this.initialDelay = initialDelay; 50 | this.multiplier = multiplier; 51 | } 52 | 53 | @Override 54 | public Duration delay(RetryContext context) { 55 | double factor = Math.pow(multiplier, context.getRetryCount()); 56 | return DurationCalcs.safeTransform( 57 | initialDelay, 58 | (amount, dimIdx) -> DurationCalcs.toBoolean((double)Long.MAX_VALUE / Math.abs(amount) > Math.abs(factor)), 59 | (amount, dimIdx) -> (long)(amount * factor) 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/delays/FirstRetryNoDelayPolicy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Original work: copyright 2013 Tomasz Nurkiewicz 3 | * https://github.com/nurkiewicz/async-retry 4 | * 5 | * This class is based on the work create by Tomasz Nurkiewicz 6 | * under the Apache License, Version 2.0. Please see 7 | * https://github.com/nurkiewicz/async-retry/blob/master/src/main/java/com/nurkiewicz/asyncretry/backoff/FirstRetryNoDelayBackoff.java 8 | * 9 | * Modified work: copyright 2015-2021 Valery Silaev (http://vsilaev.com) 10 | * 11 | * Licensed under the Apache License, Version 2.0 (the "License"); 12 | * you may not use this file except in compliance with the License. 13 | * You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, software 18 | * distributed under the License is distributed on an "AS IS" BASIS, 19 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | * See the License for the specific language governing permissions and 21 | * limitations under the License. 22 | */ 23 | package net.tascalate.concurrent.delays; 24 | 25 | import java.time.Duration; 26 | 27 | import net.tascalate.concurrent.DelayPolicy; 28 | import net.tascalate.concurrent.RetryContext; 29 | 30 | public class FirstRetryNoDelayPolicy extends DelayPolicyWrapper { 31 | 32 | public FirstRetryNoDelayPolicy(DelayPolicy target) { 33 | super(target); 34 | } 35 | 36 | @Override 37 | public Duration delay(RetryContext context) { 38 | if (context.getRetryCount() == 0) { 39 | return Duration.ZERO; 40 | } else { 41 | return target.delay(context.overrideRetryCount(context.getRetryCount() - 1)); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/delays/FixedIntervalDelayPolicy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Original work: copyright 2013 Tomasz Nurkiewicz 3 | * https://github.com/nurkiewicz/async-retry 4 | * 5 | * This class is based on the work create by Tomasz Nurkiewicz 6 | * under the Apache License, Version 2.0. Please see 7 | * https://github.com/nurkiewicz/async-retry/blob/master/src/main/java/com/nurkiewicz/asyncretry/backoff/FixedIntervalBackoff.java 8 | * 9 | * Modified work: copyright 2015-2021 Valery Silaev (http://vsilaev.com) 10 | * 11 | * Licensed under the Apache License, Version 2.0 (the "License"); 12 | * you may not use this file except in compliance with the License. 13 | * You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, software 18 | * distributed under the License is distributed on an "AS IS" BASIS, 19 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | * See the License for the specific language governing permissions and 21 | * limitations under the License. 22 | */ 23 | package net.tascalate.concurrent.delays; 24 | 25 | import java.time.Duration; 26 | 27 | import net.tascalate.concurrent.DelayPolicy; 28 | import net.tascalate.concurrent.RetryContext; 29 | 30 | public class FixedIntervalDelayPolicy implements DelayPolicy { 31 | public static final long DEFAULT_PERIOD_MILLIS = 1000; 32 | 33 | private final Duration interval; 34 | 35 | public FixedIntervalDelayPolicy() { 36 | this(DEFAULT_PERIOD_MILLIS); 37 | } 38 | 39 | public FixedIntervalDelayPolicy(long intervalMillis) { 40 | this(Duration.ofMillis(intervalMillis)); 41 | } 42 | 43 | public FixedIntervalDelayPolicy(Duration interval) { 44 | this.interval = interval; 45 | } 46 | 47 | @Override 48 | public Duration delay(RetryContext context) { 49 | return interval; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/delays/OnFailureNoDelayPolicy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.delays; 17 | 18 | import java.time.Duration; 19 | 20 | import net.tascalate.concurrent.DelayPolicy; 21 | import net.tascalate.concurrent.RetryContext; 22 | 23 | public class OnFailureNoDelayPolicy extends DelayPolicyWrapper { 24 | 25 | public OnFailureNoDelayPolicy(DelayPolicy target) { 26 | super(target); 27 | } 28 | 29 | @Override 30 | public Duration delay(RetryContext context) { 31 | return context.getLastError() != null ? Duration.ZERO : target.delay(context); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/delays/OnSuccessNoDelayPolicy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.delays; 17 | 18 | import java.time.Duration; 19 | 20 | import net.tascalate.concurrent.DelayPolicy; 21 | import net.tascalate.concurrent.RetryContext; 22 | 23 | public class OnSuccessNoDelayPolicy extends DelayPolicyWrapper { 24 | 25 | public OnSuccessNoDelayPolicy(DelayPolicy target) { 26 | super(target); 27 | } 28 | 29 | @Override 30 | public Duration delay(RetryContext context) { 31 | return context.getLastError() == null ? Duration.ZERO : target.delay(context); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/delays/ProportionalRandomDelayPolicy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Original work: copyright 2013 Tomasz Nurkiewicz 3 | * https://github.com/nurkiewicz/async-retry 4 | * 5 | * This class is based on the work create by Tomasz Nurkiewicz 6 | * under the Apache License, Version 2.0. Please see 7 | * https://github.com/nurkiewicz/async-retry/blob/master/src/main/java/com/nurkiewicz/asyncretry/backoff/ProportionalRandomBackoff.java 8 | * 9 | * Modified work: copyright 2015-2021 Valery Silaev (http://vsilaev.com) 10 | * 11 | * Licensed under the Apache License, Version 2.0 (the "License"); 12 | * you may not use this file except in compliance with the License. 13 | * You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, software 18 | * distributed under the License is distributed on an "AS IS" BASIS, 19 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | * See the License for the specific language governing permissions and 21 | * limitations under the License. 22 | */ 23 | package net.tascalate.concurrent.delays; 24 | 25 | import java.util.Random; 26 | 27 | import net.tascalate.concurrent.DelayPolicy; 28 | 29 | public class ProportionalRandomDelayPolicy extends RandomDelayPolicy { 30 | /** 31 | * Randomly up to +/- 10% 32 | */ 33 | public static final double DEFAULT_MULTIPLIER = 0.1; 34 | 35 | private final double multiplier; 36 | 37 | public ProportionalRandomDelayPolicy(DelayPolicy target) { 38 | this(target, DEFAULT_MULTIPLIER); 39 | } 40 | 41 | public ProportionalRandomDelayPolicy(DelayPolicy target, Random random) { 42 | this(target, DEFAULT_MULTIPLIER, random); 43 | } 44 | 45 | public ProportionalRandomDelayPolicy(DelayPolicy target, double multiplier) { 46 | super(target); 47 | if (multiplier <= 0) { 48 | throw new IllegalArgumentException("Multiplier must be a positive number but was: " + multiplier); 49 | } 50 | this.multiplier = multiplier; 51 | } 52 | 53 | public ProportionalRandomDelayPolicy(DelayPolicy target, double multiplier, Random random) { 54 | super(target, random); 55 | if (multiplier <= 0) { 56 | throw new IllegalArgumentException("Multiplier must be a positive number but was: " + multiplier); 57 | } 58 | this.multiplier = multiplier; 59 | } 60 | 61 | @Override 62 | long addRandomJitter(long amount, double randomizer, int dimIdx) { 63 | double randomMultiplier = (1 - 2 * randomizer) * multiplier; 64 | return Math.max(0, (long) (amount * (1 + randomMultiplier))); 65 | } 66 | 67 | @Override 68 | boolean checkBounds(long amount, double randomizer, int dimIdx) { 69 | double randomMultiplier = (1 - 2 * randomizer) * multiplier; 70 | return (double)Long.MAX_VALUE / Math.abs(amount) > Math.abs(1 + randomMultiplier); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/delays/RandomDelayPolicy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Original work: copyright 2013 Tomasz Nurkiewicz 3 | * https://github.com/nurkiewicz/async-retry 4 | * 5 | * This class is based on the work create by Tomasz Nurkiewicz 6 | * under the Apache License, Version 2.0. Please see 7 | * https://github.com/nurkiewicz/async-retry/blob/master/src/main/java/com/nurkiewicz/asyncretry/backoff/RandomBackoff.java 8 | * 9 | * Modified work: copyright 2015-2021 Valery Silaev (http://vsilaev.com) 10 | * 11 | * Licensed under the Apache License, Version 2.0 (the "License"); 12 | * you may not use this file except in compliance with the License. 13 | * You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, software 18 | * distributed under the License is distributed on an "AS IS" BASIS, 19 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | * See the License for the specific language governing permissions and 21 | * limitations under the License. 22 | */ 23 | package net.tascalate.concurrent.delays; 24 | 25 | import java.time.Duration; 26 | import java.util.Random; 27 | import java.util.concurrent.ThreadLocalRandom; 28 | import java.util.function.Supplier; 29 | 30 | import net.tascalate.concurrent.DelayPolicy; 31 | import net.tascalate.concurrent.RetryContext; 32 | 33 | abstract public class RandomDelayPolicy extends DelayPolicyWrapper { 34 | private final Supplier randomSource; 35 | 36 | protected RandomDelayPolicy(DelayPolicy target) { 37 | this(target, ThreadLocalRandom::current); 38 | } 39 | 40 | protected RandomDelayPolicy(DelayPolicy target, Random randomSource) { 41 | this(target, () -> randomSource); 42 | } 43 | 44 | private RandomDelayPolicy(DelayPolicy target, Supplier randomSource) { 45 | super(target); 46 | this.randomSource = randomSource; 47 | } 48 | 49 | @Override 50 | public Duration delay(RetryContext context) { 51 | double randomizer = random().nextDouble(); 52 | return DurationCalcs.safeTransform( 53 | target.delay(context), 54 | (amount, dimIdx) -> DurationCalcs.toBoolean(checkBounds(amount, randomizer, (int)dimIdx)), 55 | (amount, dimIdx) -> addRandomJitter(amount, randomizer, (int)dimIdx) 56 | ); 57 | } 58 | 59 | abstract long addRandomJitter(long amount, double randomizer, int dimIdx); 60 | abstract boolean checkBounds(long amount, double randomizer, int dimIdx); 61 | 62 | protected Random random() { 63 | return randomSource.get(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/delays/UniformRandomDelayPolicy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Original work: copyright 2013 Tomasz Nurkiewicz 3 | * https://github.com/nurkiewicz/async-retry 4 | * 5 | * This class is based on the work create by Tomasz Nurkiewicz 6 | * under the Apache License, Version 2.0. Please see 7 | * https://github.com/nurkiewicz/async-retry/blob/master/src/main/java/com/nurkiewicz/asyncretry/backoff/UniformRandomBackoff.java 8 | * 9 | * Modified work: copyright 2015-2021 Valery Silaev (http://vsilaev.com) 10 | * 11 | * Licensed under the Apache License, Version 2.0 (the "License"); 12 | * you may not use this file except in compliance with the License. 13 | * You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, software 18 | * distributed under the License is distributed on an "AS IS" BASIS, 19 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | * See the License for the specific language governing permissions and 21 | * limitations under the License. 22 | */ 23 | package net.tascalate.concurrent.delays; 24 | 25 | import java.time.Duration; 26 | import java.util.Random; 27 | 28 | import net.tascalate.concurrent.DelayPolicy; 29 | 30 | public class UniformRandomDelayPolicy extends RandomDelayPolicy { 31 | /** 32 | * Randomly between +/- 100ms 33 | */ 34 | public static final long DEFAULT_RANDOM_RANGE_MILLIS = 100; 35 | 36 | private final Duration range; 37 | 38 | public UniformRandomDelayPolicy(DelayPolicy target) { 39 | this(target, DEFAULT_RANDOM_RANGE_MILLIS); 40 | } 41 | 42 | public UniformRandomDelayPolicy(DelayPolicy target, Random random) { 43 | this(target, DEFAULT_RANDOM_RANGE_MILLIS, random); 44 | } 45 | 46 | public UniformRandomDelayPolicy(DelayPolicy target, long range) { 47 | this(target, Duration.ofMillis(range)); 48 | } 49 | 50 | public UniformRandomDelayPolicy(DelayPolicy target, Duration range) { 51 | super(target); 52 | if (!DelayPolicy.isValid(range)) { 53 | throw new IllegalArgumentException("Range must be positive but was: " + range); 54 | } 55 | this.range = range; 56 | } 57 | 58 | public UniformRandomDelayPolicy(DelayPolicy target, long range, Random random) { 59 | this(target, Duration.ofMillis(range), random); 60 | } 61 | 62 | public UniformRandomDelayPolicy(DelayPolicy target, Duration range, Random random) { 63 | super(target, random); 64 | if (!DelayPolicy.isValid(range)) { 65 | throw new IllegalArgumentException("Range must be positive but was: " + range); 66 | } 67 | this.range = range; 68 | } 69 | 70 | @Override 71 | long addRandomJitter(long amount, double randomizer, int dimIdx) { 72 | long rangeNormalized = DurationCalcs.safeExtractAmount(range, dimIdx); 73 | double uniformRandom = (1 - randomizer * 2) * rangeNormalized; 74 | return Math.max(0, (long) (amount + uniformRandom)); 75 | } 76 | 77 | @Override 78 | boolean checkBounds(long amount, double randomizer, int dimIdx) { 79 | long rangeNormalized = DurationCalcs.safeExtractAmount(range, dimIdx); 80 | double uniformRandom = (1 - randomizer * 2) * rangeNormalized; 81 | if (uniformRandom < 0) { 82 | return -Long.MAX_VALUE + Math.abs(amount) < uniformRandom; 83 | } else { 84 | return Long.MAX_VALUE - Math.abs(amount) > uniformRandom; 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/io/AsyncByteChannel.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.io; 17 | 18 | import java.nio.ByteBuffer; 19 | import java.nio.channels.AsynchronousByteChannel; 20 | import java.nio.channels.CompletionHandler; 21 | import java.util.function.BiConsumer; 22 | 23 | import net.tascalate.concurrent.Promise; 24 | 25 | public interface AsyncByteChannel extends AsynchronousByteChannel, AsyncChannel { 26 | @Override 27 | Promise read(ByteBuffer dst); 28 | @Override 29 | Promise write(ByteBuffer src); 30 | 31 | public static CompletionHandler handler(BiConsumer handler) { 32 | return new CompletionHandler() { 33 | @Override 34 | public void completed(V result, Object attachment) { 35 | handler.accept(result, null); 36 | } 37 | 38 | @Override 39 | public void failed(Throwable ex, Object attachment) { 40 | handler.accept(null, ex); 41 | } 42 | }; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/io/AsyncChannel.java: -------------------------------------------------------------------------------- 1 | package net.tascalate.concurrent.io; 2 | 3 | import java.nio.channels.AsynchronousChannel; 4 | 5 | public interface AsyncChannel extends AsynchronousChannel { 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/io/AsyncFileChannel.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.io; 17 | 18 | import java.io.IOException; 19 | import java.nio.channels.AsynchronousFileChannel; 20 | import java.nio.file.OpenOption; 21 | import java.nio.file.Path; 22 | import java.nio.file.attribute.FileAttribute; 23 | import java.util.Collections; 24 | import java.util.HashSet; 25 | import java.util.Objects; 26 | import java.util.Set; 27 | import java.util.concurrent.ExecutorService; 28 | 29 | public class AsyncFileChannel extends AbstractAsyncFileChannel { 30 | 31 | protected AsyncFileChannel(AsynchronousFileChannel delegate) { 32 | super(delegate); 33 | } 34 | 35 | public static AsyncFileChannel open(Path file, ExecutorService executor, OpenOption... options) throws IOException { 36 | Set set; 37 | if (options.length == 0) { 38 | set = Collections.emptySet(); 39 | } else { 40 | set = new HashSet<>(); 41 | Collections.addAll(set, options); 42 | } 43 | return open(file, executor, set, NO_ATTRIBUTES); 44 | } 45 | 46 | public static AsyncFileChannel open(Path file, 47 | ExecutorService executor, 48 | Set options, 49 | FileAttribute... attrs) throws IOException { 50 | Objects.requireNonNull(executor, "Executor should be specified"); 51 | return new AsyncFileChannel(AsynchronousFileChannel.open(file, options, executor, attrs)); 52 | } 53 | 54 | 55 | private static final FileAttribute[] NO_ATTRIBUTES = new FileAttribute[0]; 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/io/AsyncResult.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.io; 17 | 18 | import java.nio.channels.CompletionHandler; 19 | 20 | import net.tascalate.concurrent.CompletablePromise; 21 | 22 | class AsyncResult extends CompletablePromise { 23 | 24 | final CompletionHandler handler = new CompletionHandler() { 25 | @Override 26 | public void completed(final V result, final Object attachment) { 27 | success(result); 28 | } 29 | 30 | @Override 31 | public void failed(Throwable exc, Object attachment) { 32 | failure(exc); 33 | } 34 | }; 35 | 36 | AsyncResult() { 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/io/AsyncServerSocketChannel.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.io; 17 | 18 | import java.io.IOException; 19 | import java.nio.channels.AsynchronousChannelGroup; 20 | import java.nio.channels.AsynchronousServerSocketChannel; 21 | import java.nio.channels.AsynchronousSocketChannel; 22 | import java.util.Objects; 23 | 24 | public class AsyncServerSocketChannel extends AbstractAsyncServerSocketChannel { 25 | 26 | protected AsyncServerSocketChannel(AsynchronousServerSocketChannel delegate) { 27 | super(delegate); 28 | } 29 | 30 | public static AsyncServerSocketChannel open(AsynchronousChannelGroup group) throws IOException { 31 | Objects.requireNonNull(group, "ChannelGroup should be specified"); 32 | return new AsyncServerSocketChannel(AsynchronousServerSocketChannel.open(group)); 33 | } 34 | 35 | @Override 36 | protected AsyncSocketChannel wrap(AsynchronousSocketChannel channel) { 37 | return new AsyncSocketChannel(channel); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/io/AsyncSocketChannel.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.io; 17 | 18 | import java.io.IOException; 19 | import java.nio.channels.AsynchronousChannelGroup; 20 | import java.nio.channels.AsynchronousSocketChannel; 21 | import java.util.Objects; 22 | 23 | public class AsyncSocketChannel extends AbstractAsyncSocketChannel { 24 | 25 | protected AsyncSocketChannel(AsynchronousSocketChannel delegate) { 26 | super(delegate); 27 | } 28 | 29 | public static AsyncSocketChannel open(AsynchronousChannelGroup group) throws IOException { 30 | Objects.requireNonNull(group, "ChannelGroup should be specified"); 31 | return new AsyncSocketChannel(AsynchronousSocketChannel.open(group)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/io/BlockingThreadSelector.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.io; 17 | 18 | import java.nio.channels.SelectionKey; 19 | import java.nio.channels.Selector; 20 | import java.nio.channels.spi.AbstractSelectableChannel; 21 | import java.nio.channels.spi.AbstractSelector; 22 | import java.util.Set; 23 | 24 | final class BlockingThreadSelector extends AbstractSelector { 25 | 26 | private final Runnable interruptHandler; 27 | 28 | BlockingThreadSelector(Runnable interruptHandler) { 29 | super(null); 30 | this.interruptHandler = interruptHandler; 31 | } 32 | 33 | void enter() { 34 | begin(); 35 | } 36 | 37 | void exit() { 38 | end(); 39 | } 40 | 41 | @Override 42 | protected void implCloseSelector() { 43 | 44 | } 45 | 46 | @Override 47 | protected SelectionKey register(AbstractSelectableChannel ch, int ops, Object att) { 48 | return unimplemented(); 49 | } 50 | 51 | @Override 52 | public Set keys() { 53 | return unimplemented(); 54 | } 55 | 56 | @Override 57 | public int select() { 58 | return unimplemented(); 59 | } 60 | 61 | @Override 62 | public int select(long timeout) { 63 | return unimplemented(); 64 | } 65 | 66 | @Override 67 | public int selectNow() { 68 | return unimplemented(); 69 | } 70 | 71 | @Override 72 | public Set selectedKeys() { 73 | return unimplemented(); 74 | } 75 | 76 | @Override 77 | public Selector wakeup() { 78 | interruptHandler.run(); 79 | return this; 80 | } 81 | 82 | private static T unimplemented() { 83 | throw new UnsupportedOperationException(); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/io/InterruptiblePromiseCustomizer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.io; 17 | 18 | import java.util.function.BiConsumer; 19 | import java.util.function.BiFunction; 20 | import java.util.function.Consumer; 21 | import java.util.function.Function; 22 | import java.util.function.Predicate; 23 | import java.util.function.Supplier; 24 | 25 | import net.tascalate.concurrent.decorators.PromiseCustomizer; 26 | 27 | public class InterruptiblePromiseCustomizer implements PromiseCustomizer { 28 | 29 | @Override 30 | public Runnable wrapArgument(Runnable original, boolean async) { 31 | return BlockingIO.interruptible(original); 32 | } 33 | 34 | @Override 35 | public Function wrapArgument(Function original, boolean async, boolean isCompose) { 36 | return BlockingIO.interruptible(original); 37 | } 38 | 39 | @Override 40 | public Consumer wrapArgument(Consumer original, boolean async) { 41 | return BlockingIO.interruptible(original); 42 | } 43 | 44 | @Override 45 | public Supplier wrapArgument(Supplier original, boolean async) { 46 | return BlockingIO.interruptible(original); 47 | } 48 | 49 | @Override 50 | public Predicate wrapArgument(Predicate original, boolean async) { 51 | return BlockingIO.interruptible(original); 52 | } 53 | 54 | @Override 55 | public BiFunction wrapArgument(BiFunction original, boolean async) { 56 | return BlockingIO.interruptible(original); 57 | } 58 | 59 | @Override 60 | public BiConsumer wrapArgument(BiConsumer original, boolean async) { 61 | return BlockingIO.interruptible(original); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/locks/AbstractAsyncLock.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.locks; 17 | 18 | import java.util.Optional; 19 | 20 | import net.tascalate.concurrent.Promise; 21 | 22 | public interface AbstractAsyncLock { 23 | /** 24 | * Attempts to immediately acquire the lock, returning a populated {@link Optional} if the lock is 25 | * not currently held. 26 | * 27 | * @return An {@link Optional} holding a {@link Token} if the lock is not held; otherwise an 28 | * empty Optional 29 | */ 30 | Optional tryAcquire(); 31 | 32 | /** 33 | * Exclusively acquires this lock. The returned stage will complete when the 34 | * lock is exclusively acquired by this caller. The stage may already be 35 | * complete if the lock was not held. 36 | * 37 | *

38 | * The {@link Token} held by the returned stage is used to release the 39 | * lock after it has been acquired and the lock-protected action has 40 | * completed. 41 | * 42 | * @return A {@link Promise} which will complete with a 43 | * {@link Token} when the lock has been exclusively acquired 44 | */ 45 | Promise acquire(); 46 | 47 | /** 48 | * A lock token indicating that the associated lock has been exclusively 49 | * acquired. Once the protected action is completed, the lock may be 50 | * released by calling {@link Token#release()} 51 | */ 52 | interface Token extends AutoCloseable { 53 | /** Releases this lock, allowing others to acquire it. */ 54 | void release(); 55 | 56 | /** 57 | * Releases this lock, allowing others to acquire it. 58 | * 59 | *

60 | * {@inheritDoc} 61 | */ 62 | @Override 63 | default void close() { 64 | release(); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/locks/AsyncCountDownLatch.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.locks; 17 | 18 | import java.util.concurrent.atomic.AtomicLongFieldUpdater; 19 | import java.util.function.LongBinaryOperator; 20 | 21 | import net.tascalate.concurrent.CompletableFutureWrapper; 22 | 23 | public class AsyncCountDownLatch extends CompletableFutureWrapper { 24 | private static final AtomicLongFieldUpdater COUNT_UPDATER = 25 | AtomicLongFieldUpdater.newUpdater(AsyncCountDownLatch.class, "count"); 26 | private static final LongBinaryOperator DECREMENT = 27 | (prev, change) -> Math.max(0, prev - change); 28 | 29 | private final long initial; 30 | 31 | @SuppressWarnings("unused") 32 | private volatile long count; 33 | 34 | public AsyncCountDownLatch(long count) { 35 | if (count < 0) { 36 | throw new IllegalArgumentException("count < 0 : " + count); 37 | } 38 | this.count = this.initial = count; 39 | if (count == 0) { 40 | success(count); 41 | } 42 | } 43 | 44 | public long countDown() { 45 | return countDown(1); 46 | } 47 | 48 | public long countDown(long delta) { 49 | if (delta <= 0) { 50 | throw new IllegalArgumentException("delta <= 0 : " + delta); 51 | } 52 | long current = COUNT_UPDATER.getAndAccumulate(this, delta, DECREMENT); 53 | if (current == 0) { 54 | success(initial); 55 | } 56 | return current; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/locks/AsyncLock.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.locks; 17 | 18 | /** 19 | * An asynchronously acquirable mutual exclusion lock. 20 | * 21 | */ 22 | public interface AsyncLock extends AbstractAsyncLock { 23 | 24 | interface Token extends AbstractAsyncLock.Token {} 25 | 26 | static AsyncLock create() { 27 | return new DefaultAsyncLock(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/locks/AsyncSemaphoreLock.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.locks; 17 | 18 | import java.util.Optional; 19 | 20 | import net.tascalate.concurrent.Promise; 21 | 22 | public interface AsyncSemaphoreLock extends AbstractAsyncLock { 23 | 24 | public long availablePermits(); 25 | 26 | public int getQueueLength(); 27 | 28 | public Optional drainPermits(); 29 | 30 | default public Optional tryAcquire() { 31 | return tryAcquire(1L); 32 | } 33 | 34 | public Optional tryAcquire(long permits); 35 | 36 | default public Promise acquire() { 37 | return acquire(1L); 38 | } 39 | 40 | public Promise acquire(long permits); 41 | 42 | /** 43 | * A semaphore token indicating that necessary permit has been exclusively acquired. Once the 44 | * protected action is completed, permits may be released by calling 45 | * {@link Token#release()} 46 | */ 47 | interface Token extends AsyncLock.Token { 48 | long permits(); 49 | } 50 | 51 | static AsyncSemaphoreLock create(long permits, boolean fair) { 52 | return new DefaultAsyncSemaphoreLock(permits, fair); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/locks/DefaultAsyncLock.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.locks; 17 | 18 | import java.util.Optional; 19 | import java.util.Queue; 20 | import java.util.concurrent.ConcurrentLinkedQueue; 21 | import java.util.concurrent.atomic.AtomicBoolean; 22 | 23 | import net.tascalate.concurrent.CompletableFutureWrapper; 24 | import net.tascalate.concurrent.Promise; 25 | 26 | public class DefaultAsyncLock implements AsyncLock { 27 | final Queue waiters = new ConcurrentLinkedQueue<>(); 28 | 29 | @Override 30 | public Optional tryAcquire() { 31 | Promise lock = acquire(); 32 | if (lock.isDone()) { 33 | return Optional.of(lock.join()); 34 | } else { 35 | waiters.remove(lock); 36 | return Optional.empty(); 37 | } 38 | } 39 | 40 | @Override 41 | public Promise acquire() { 42 | LockPromise promise = new LockPromise(); 43 | waiters.add(promise); // Add to tail; 44 | 45 | nextWaiter(); 46 | 47 | return promise; 48 | } 49 | 50 | private void nextWaiter() { 51 | LockPromise head = waiters.peek(); // Peek from head 52 | if (head != null) { 53 | head.acquire(); 54 | } 55 | } 56 | 57 | @Override 58 | public String toString() { 59 | LockPromise head = waiters.peek(); 60 | boolean isAcquired = head != null & head.isDone(); 61 | int size = Math.max(0, waiters.size() - (isAcquired ? 1 : 0)); 62 | return String.format( 63 | "%s(acquired=%s, queueSize=%d)", 64 | getClass().getSimpleName(), isAcquired, size 65 | ); 66 | } 67 | 68 | private class LockPromise extends CompletableFutureWrapper { 69 | private final AtomicBoolean released = new AtomicBoolean(); 70 | private final Token token = this::release; 71 | 72 | @Override 73 | public boolean cancel(boolean mayInterruptIfRunning) { 74 | if (super.cancel(mayInterruptIfRunning)) { 75 | waiters.remove(this); 76 | return true; 77 | } else { 78 | return false; 79 | } 80 | } 81 | 82 | boolean acquire() { 83 | return success(token); 84 | } 85 | 86 | void release() { 87 | if (released.compareAndSet(false, true)) { 88 | LockPromise released = waiters.poll(); // Remove current head 89 | if (this != released) { 90 | throw new IllegalStateException("The released lock is not equal to the top of the queue"); 91 | } 92 | nextWaiter(); 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/locks/DefaultAsyncSemaphore.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.locks; 17 | 18 | public class DefaultAsyncSemaphore extends AsyncSemaphoreBase 19 | implements AsyncSemaphore { 20 | 21 | public DefaultAsyncSemaphore(long totalPermits, boolean fair) { 22 | super(totalPermits, fair); 23 | } 24 | 25 | @Override 26 | public long drainPermits() { 27 | return drainPermitsInternal(); 28 | } 29 | 30 | @Override 31 | public boolean tryAcquire(long permits) { 32 | return tryAcquireInternal(permits); 33 | } 34 | 35 | @Override 36 | public void release(long permits) { 37 | super.release(permits); 38 | } 39 | 40 | @Override 41 | protected Long createPromisePayload(long permits) { 42 | return Long.valueOf(permits); 43 | } 44 | 45 | @Override 46 | protected SemaphorePromise createPromise(long permits) { 47 | return new SemaphorePromise(permits); 48 | } 49 | 50 | private class SemaphorePromise extends AbstractSemaphorePromise { 51 | private final Long permits; 52 | 53 | SemaphorePromise(long permits) { 54 | this.permits = Long.valueOf(permits); 55 | } 56 | 57 | long permits() { 58 | return permits.longValue(); 59 | } 60 | 61 | boolean acquire() { 62 | return success(permits); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/locks/DefaultAsyncSemaphoreLock.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.locks; 17 | 18 | import java.util.Optional; 19 | import java.util.concurrent.atomic.AtomicBoolean; 20 | 21 | public class DefaultAsyncSemaphoreLock extends AsyncSemaphoreBase 22 | implements AsyncSemaphoreLock { 23 | 24 | public DefaultAsyncSemaphoreLock(long totalPermits, boolean fair) { 25 | super(totalPermits, fair); 26 | } 27 | 28 | @Override 29 | public Optional drainPermits() { 30 | long acquired = drainPermitsInternal(); 31 | return acquired > 0 ? Optional.of(createPromisePayload(acquired)) : Optional.empty(); 32 | } 33 | 34 | @Override 35 | public Optional tryAcquire(long permitsCount) { 36 | if (tryAcquireInternal(permitsCount)) { 37 | return Optional.of(createPromisePayload(permitsCount)); 38 | } else { 39 | return Optional.empty(); 40 | } 41 | } 42 | 43 | @Override 44 | protected Token createPromisePayload(long permits) { 45 | return new AbstractToken(permits) { 46 | private final AtomicBoolean released = new AtomicBoolean(); 47 | @Override 48 | public void release() { 49 | if (released.compareAndSet(false, true)) { 50 | DefaultAsyncSemaphoreLock.this.release(permits()); 51 | } 52 | } 53 | }; 54 | } 55 | 56 | @Override 57 | protected LockPromise createPromise(long permitsCount) { 58 | return new LockPromise(permitsCount); 59 | } 60 | 61 | abstract static class AbstractToken implements Token { 62 | private final long permitsCount; 63 | 64 | public AbstractToken(long permitsCount) { 65 | this.permitsCount = permitsCount; 66 | } 67 | 68 | @Override 69 | public long permits() { 70 | return permitsCount; 71 | } 72 | } 73 | 74 | private class LockPromise extends AbstractSemaphorePromise { 75 | private final AtomicBoolean released = new AtomicBoolean(); 76 | private final Token token; 77 | 78 | LockPromise(long permitsCount) { 79 | this.token = new AbstractToken(permitsCount) { 80 | @Override 81 | public void release() { 82 | LockPromise.this.release(); 83 | } 84 | }; 85 | } 86 | 87 | long permits() { 88 | return token.permits(); 89 | } 90 | 91 | boolean acquire() { 92 | return success(token); 93 | } 94 | 95 | void release() { 96 | if (released.compareAndSet(false, true)) { 97 | DefaultAsyncSemaphoreLock.this.release(permits()); 98 | } 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/var/ContextVar.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2025 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.var; 17 | 18 | import java.util.Arrays; 19 | import java.util.List; 20 | import java.util.concurrent.Callable; 21 | import java.util.function.Supplier; 22 | 23 | public interface ContextVar { 24 | T get(); 25 | 26 | V callWith(T capturedValue, Callable code) throws Exception; 27 | 28 | default void runWith(T capturedValue, Runnable code) { 29 | try { 30 | callWith(capturedValue, () -> { 31 | code.run(); 32 | return null; 33 | }); 34 | } catch (Error | RuntimeException ex) { 35 | throw ex; 36 | } catch (Exception ex) { 37 | throw new RuntimeException(ex); 38 | } 39 | } 40 | 41 | default V supplyWith(T capturedValue, Supplier code) { 42 | try { 43 | return callWith(capturedValue, code::get); 44 | } catch (Error | RuntimeException ex) { 45 | throw ex; 46 | } catch (Exception ex) { 47 | throw new RuntimeException(ex); 48 | } 49 | } 50 | 51 | default ContextTrampoline relay() { 52 | return new ContextTrampoline(this); 53 | } 54 | 55 | /** 56 | * Convert this var to the variant with pessimistic propagation option. 57 | * Useful for rare cases when thread might have its own default values 58 | * of the context variables and they must be restored. 59 | *

The logic is the following:

60 | *
    61 | *
  1. Save context variables from the current thread
  2. 62 | *
  3. Apply context variables from the snapshot
  4. 63 | *
  5. Execute code
  6. 64 | *
  7. Restore context variables saved in the step [1]
  8. 65 | *
66 | */ 67 | default ContextVar strict() { 68 | return this; 69 | } 70 | 71 | /** 72 | * Convert this var to the variant that is optimized for performance 73 | *

The logic is the following:

74 | *
    75 | *
  1. Apply context variables from the snapshot
  2. 76 | *
  3. Execute code
  4. 77 | *
  5. Reset context variables
  6. 78 | *
79 | */ 80 | default ContextVar optimized() { 81 | return this; 82 | } 83 | 84 | 85 | @SuppressWarnings("unchecked") 86 | public static ContextVar empty() { 87 | return (ContextVar)ContextVarHelper.EMPTY; 88 | } 89 | 90 | @SafeVarargs 91 | public static ContextVar> of(ContextVar... vars) { 92 | return of(Arrays.asList(vars)); 93 | } 94 | 95 | public static ContextVar> of(List> vars) { 96 | if (null == vars || vars.isEmpty()) { 97 | return ContextVar.empty(); 98 | } else { 99 | return new ContextVarGroup<>(vars); 100 | } 101 | } 102 | 103 | @SafeVarargs 104 | public static ContextVar> ofMorphing(ContextVar... vars) { 105 | return ofMorphing(Arrays.asList(vars)); 106 | } 107 | 108 | public static ContextVar> ofMorphing(List> vars) { 109 | if (null == vars || vars.isEmpty()) { 110 | return ContextVar.empty(); 111 | } else { 112 | return new ContextVarGroup.Optimized<>(vars); 113 | } 114 | } 115 | } -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/var/ContextVarGroup.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2025 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.var; 17 | 18 | import java.util.Iterator; 19 | import java.util.List; 20 | import java.util.concurrent.Callable; 21 | import java.util.stream.Collectors; 22 | 23 | class ContextVarGroup implements ContextVar> { 24 | 25 | final List> vars; 26 | 27 | ContextVarGroup(List> vars) { 28 | this.vars = vars; 29 | } 30 | 31 | @Override 32 | public List get() { 33 | return vars.stream() 34 | .map(ContextVar::get) 35 | .collect(Collectors.toList()); 36 | } 37 | 38 | @Override 39 | public V callWith(List capturedValue, Callable code) throws Exception { 40 | Iterator> varsIterator = vars.iterator(); 41 | Iterator valuesIterator = capturedValue.iterator(); 42 | Callable wrappedCode = code; 43 | while (varsIterator.hasNext() && valuesIterator.hasNext()) { 44 | @SuppressWarnings("unchecked") 45 | ContextVar contextVar = (ContextVar)varsIterator.next(); 46 | T contextVal = valuesIterator.next(); 47 | wrappedCode = ContextVarHelper.nextCallable(contextVar, contextVal, wrappedCode); 48 | } 49 | return code.call(); 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | return String.format("%s", vars); 55 | } 56 | 57 | static final class Strict extends ContextVarGroup { 58 | Strict(List> vars) { 59 | super(ContextVarHelper.convertList(vars, ContextVar::strict)); 60 | } 61 | 62 | @Override 63 | public Strict strict() { 64 | return this; 65 | } 66 | 67 | @Override 68 | public Optimized optimized() { 69 | return new Optimized<>(vars); 70 | } 71 | 72 | @Override 73 | public String toString() { 74 | return String.format("%s", vars); 75 | } 76 | } 77 | 78 | static final class Optimized extends ContextVarGroup { 79 | Optimized(List> vars) { 80 | super(ContextVarHelper.convertList(vars, ContextVar::optimized)); 81 | } 82 | 83 | @Override 84 | public Strict strict() { 85 | return new Strict<>(vars); 86 | } 87 | 88 | @Override 89 | public Optimized optimized() { 90 | return this; 91 | } 92 | 93 | @Override 94 | public String toString() { 95 | return String.format("%s", vars); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/var/ContextVarHelper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2025 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.var; 17 | 18 | import java.util.Iterator; 19 | import java.util.List; 20 | import java.util.concurrent.Callable; 21 | import java.util.concurrent.atomic.AtomicLong; 22 | import java.util.function.Function; 23 | import java.util.stream.Collectors; 24 | 25 | class ContextVarHelper { 26 | 27 | static String generateVarName() { 28 | return ""; 29 | } 30 | 31 | static void applyValues(List> threadLocals, List data) { 32 | Iterator> vars = threadLocals.iterator(); 33 | Iterator values = data.iterator(); 34 | while (vars.hasNext() && values.hasNext()) { 35 | @SuppressWarnings("unchecked") 36 | ModifiableContextVar contextVar = (ModifiableContextVar)vars.next(); 37 | T contextVal = values.next(); 38 | if (null == contextVal) { 39 | contextVar.remove(); 40 | } else { 41 | contextVar.set(contextVal); 42 | } 43 | } 44 | } 45 | 46 | static Callable nextCallable(ContextVar contextVar, T contextValue, Callable originalCode) { 47 | return new Callable() { 48 | @Override 49 | public V call() throws Exception { 50 | return contextVar.callWith(contextValue, originalCode); 51 | } 52 | }; 53 | } 54 | 55 | static List convertList(List list, Function mapper) { 56 | return list.stream().map(mapper).collect(Collectors.toList()); 57 | } 58 | 59 | static final ContextVar EMPTY = new ContextVar() { 60 | @Override 61 | public Object get() { 62 | throw new UnsupportedOperationException(); 63 | } 64 | 65 | @Override 66 | public V callWith(Object capturedValue, Callable code) throws Exception { 67 | return code.call(); 68 | } 69 | 70 | @Override 71 | public String toString() { 72 | return ""; 73 | } 74 | }; 75 | 76 | private static final AtomicLong COUNTER = new AtomicLong(); 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/var/ContextualExecutor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.var; 17 | 18 | import java.util.concurrent.Executor; 19 | 20 | class ContextualExecutor implements Executor { 21 | protected final D delegate; 22 | protected final Contextualization ctxz; 23 | 24 | ContextualExecutor(D delegate, Contextualization ctxz) { 25 | this.delegate = delegate; 26 | this.ctxz = ctxz; 27 | } 28 | 29 | @Override 30 | public void execute(Runnable command) { 31 | delegate.execute(contextualRunnable(command)); 32 | } 33 | 34 | Runnable contextualRunnable(Runnable original) { 35 | return () -> ctxz.runWith(original); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/var/ContextualExecutorService.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.var; 17 | 18 | import java.util.Collection; 19 | import java.util.List; 20 | import java.util.concurrent.Callable; 21 | import java.util.concurrent.ExecutionException; 22 | import java.util.concurrent.ExecutorService; 23 | import java.util.concurrent.Future; 24 | import java.util.concurrent.TimeUnit; 25 | import java.util.concurrent.TimeoutException; 26 | import java.util.stream.Collectors; 27 | 28 | class ContextualExecutorService 29 | extends ContextualExecutor 30 | implements ExecutorService { 31 | 32 | ContextualExecutorService(D delegate, Contextualization ctxz) { 33 | super(delegate, ctxz); 34 | } 35 | 36 | @Override 37 | public void shutdown() { 38 | delegate.shutdown(); 39 | } 40 | 41 | @Override 42 | public List shutdownNow() { 43 | return delegate.shutdownNow(); 44 | } 45 | 46 | @Override 47 | public boolean isShutdown() { 48 | return delegate.isShutdown(); 49 | } 50 | 51 | @Override 52 | public boolean isTerminated() { 53 | return delegate.isTerminated(); 54 | } 55 | 56 | @Override 57 | public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { 58 | return delegate.awaitTermination(timeout, unit); 59 | } 60 | 61 | @Override 62 | public Future submit(Callable task) { 63 | return delegate.submit(contextualCallable(task)); 64 | } 65 | 66 | @Override 67 | public Future submit(Runnable task, T result) { 68 | return delegate.submit(contextualRunnable(task), result); 69 | } 70 | 71 | @Override 72 | public Future submit(Runnable task) { 73 | return delegate.submit(contextualRunnable(task)); 74 | } 75 | 76 | @Override 77 | public List> invokeAll(Collection> tasks) throws InterruptedException { 78 | return delegate.invokeAll(contextualCallables(tasks)); 79 | } 80 | 81 | @Override 82 | public List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) 83 | throws InterruptedException { 84 | return delegate.invokeAll(contextualCallables(tasks), timeout, unit); 85 | } 86 | 87 | @Override 88 | public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException { 89 | return delegate.invokeAny(contextualCallables(tasks)); 90 | } 91 | 92 | @Override 93 | public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) 94 | throws InterruptedException, ExecutionException, TimeoutException { 95 | return delegate.invokeAny(contextualCallables(tasks), timeout, unit); 96 | } 97 | 98 | private Collection> contextualCallables(Collection> tasks) { 99 | return tasks.stream().map(this::contextualCallable).collect(Collectors.toList()); 100 | } 101 | 102 | Callable contextualCallable(Callable original) { 103 | return () -> ctxz.callWith(original); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/var/ContextualPromiseCustomizer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.var; 17 | 18 | import java.util.function.BiConsumer; 19 | import java.util.function.BiFunction; 20 | import java.util.function.Consumer; 21 | import java.util.function.Function; 22 | import java.util.function.Predicate; 23 | import java.util.function.Supplier; 24 | 25 | import net.tascalate.concurrent.decorators.PromiseCustomizer; 26 | 27 | class ContextualPromiseCustomizer implements PromiseCustomizer { 28 | private final Contextualization ctxz; 29 | 30 | ContextualPromiseCustomizer(Contextualization ctxz) { 31 | this.ctxz = ctxz; 32 | } 33 | 34 | @Override 35 | public Runnable wrapArgument(Runnable original, boolean async) { 36 | return () -> ctxz.runWith(original); 37 | } 38 | 39 | @Override 40 | public Function wrapArgument(Function original, boolean async, boolean isCompose) { 41 | return u -> ctxz.supplyWith(() -> original.apply(u)); 42 | } 43 | 44 | @Override 45 | public Consumer wrapArgument(Consumer original, boolean async) { 46 | return u -> ctxz.runWith(() -> original.accept(u)); 47 | } 48 | 49 | @Override 50 | public Supplier wrapArgument(Supplier original, boolean async) { 51 | return () -> ctxz.supplyWith(original); 52 | } 53 | 54 | @Override 55 | public Predicate wrapArgument(Predicate original, boolean async) { 56 | return u -> ctxz.supplyWith(() -> original.test(u)); 57 | } 58 | 59 | @Override 60 | public BiFunction wrapArgument(BiFunction original, boolean async) { 61 | return (u, v) -> ctxz.supplyWith(() -> original.apply(u, v)); 62 | } 63 | 64 | @Override 65 | public BiConsumer wrapArgument(BiConsumer original, boolean async) { 66 | return (u, v) -> ctxz.runWith(() -> original.accept(u, v)); 67 | } 68 | 69 | /* 70 | @Override 71 | public CompletionStage wrapArgument(CompletionStage original, boolean async) { 72 | // TODO Option to wrap original CompletionStage? 73 | return PromiseCustomizer.super.wrapArgument(original, async); 74 | } 75 | */ 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/var/ContextualScheduledExecutorService.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.var; 17 | 18 | import java.util.concurrent.Callable; 19 | import java.util.concurrent.ScheduledExecutorService; 20 | import java.util.concurrent.ScheduledFuture; 21 | import java.util.concurrent.TimeUnit; 22 | 23 | class ContextualScheduledExecutorService 24 | extends ContextualExecutorService 25 | implements ScheduledExecutorService { 26 | 27 | ContextualScheduledExecutorService(D delegate, Contextualization ctxz) { 28 | super(delegate, ctxz); 29 | } 30 | 31 | @Override 32 | public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { 33 | return delegate.schedule(contextualRunnable(command), delay, unit); 34 | } 35 | 36 | @Override 37 | public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) { 38 | return delegate.schedule(contextualCallable(callable), delay, unit); 39 | } 40 | 41 | @Override 42 | public ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { 43 | return delegate.scheduleWithFixedDelay(contextualRunnable(command), initialDelay, period, unit); 44 | } 45 | 46 | @Override 47 | public ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { 48 | return delegate.scheduleWithFixedDelay(contextualRunnable(command), initialDelay, delay, unit); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/var/ContextualTaskExecutorService.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.var; 17 | 18 | import java.util.concurrent.Callable; 19 | 20 | import net.tascalate.concurrent.Promise; 21 | import net.tascalate.concurrent.TaskExecutorService; 22 | 23 | class ContextualTaskExecutorService 24 | extends ContextualExecutorService 25 | implements TaskExecutorService { 26 | 27 | ContextualTaskExecutorService(D delegate, Contextualization ctxz) { 28 | super(delegate, ctxz); 29 | } 30 | 31 | @Override 32 | public Promise submit(Callable task) { 33 | return (Promise)super.submit(task); 34 | } 35 | 36 | @Override 37 | public Promise submit(Runnable task, T result) { 38 | return (Promise)super.submit(task, result); 39 | } 40 | 41 | @Override 42 | public Promise submit(Runnable task) { 43 | return (Promise)super.submit(task); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/var/Contextualization.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.var; 17 | 18 | import java.util.concurrent.Callable; 19 | import java.util.function.Supplier; 20 | 21 | class Contextualization { 22 | private final ContextVar contextVar; 23 | private final T capturedContext; 24 | 25 | Contextualization(ContextVar contextVar, 26 | T capturedContext) { 27 | 28 | this.contextVar = null == contextVar ? ContextVar.empty() : contextVar; 29 | this.capturedContext = capturedContext; 30 | } 31 | 32 | void runWith(Runnable code) { 33 | contextVar.runWith(capturedContext, code); 34 | } 35 | 36 | V supplyWith(Supplier code) { 37 | return contextVar.supplyWith(capturedContext, code); 38 | } 39 | 40 | V callWith(Callable code) throws Exception { 41 | return contextVar.callWith(capturedContext, code); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/var/CustomModifiableContextVar.java: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Copyright 2015-2025 Valery Silaev (http://vsilaev.com) 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package net.tascalate.concurrent.var; 18 | 19 | import java.util.concurrent.Callable; 20 | import java.util.function.Consumer; 21 | import java.util.function.Supplier; 22 | 23 | abstract class CustomModifiableContextVar implements ModifiableContextVar { 24 | final String name; 25 | 26 | final Supplier reader; 27 | final Consumer writer; 28 | final Runnable eraser; 29 | 30 | CustomModifiableContextVar(String name, Supplier reader, Consumer writer, Runnable eraser) { 31 | this.name = name; 32 | this.reader = reader; 33 | this.writer = writer; 34 | this.eraser = eraser; 35 | } 36 | 37 | @Override 38 | public T get() { 39 | return reader.get(); 40 | } 41 | 42 | @Override 43 | public void set(T value) { 44 | writer.accept(value); 45 | } 46 | 47 | @Override 48 | public void remove() { 49 | if (null != eraser) { 50 | eraser.run(); 51 | } else { 52 | set(null); 53 | } 54 | } 55 | 56 | static final class Strict extends CustomModifiableContextVar { 57 | Strict(String name, Supplier reader, Consumer writer, Runnable eraser) { 58 | super(name, reader, writer, eraser); 59 | } 60 | 61 | @Override 62 | public V callWith(T capturedValue, Callable code) throws Exception { 63 | T prevValue = this.get(); 64 | try { 65 | return code.call(); 66 | } finally { 67 | if (null == prevValue) { 68 | remove(); 69 | } else { 70 | set(prevValue); 71 | } 72 | } 73 | } 74 | 75 | @Override 76 | public Strict strict() { 77 | return this; 78 | } 79 | 80 | @Override 81 | public Optimized optimized() { 82 | return new Optimized<>(name, reader, writer, eraser); 83 | } 84 | 85 | @Override 86 | public String toString() { 87 | return String.format("[%s]", name); 88 | } 89 | } 90 | 91 | static final class Optimized extends CustomModifiableContextVar { 92 | Optimized(String name, Supplier reader, Consumer writer, Runnable eraser) { 93 | super(name, reader, writer, eraser); 94 | } 95 | 96 | @Override 97 | public V callWith(T capturedValue, Callable code) throws Exception { 98 | try { 99 | return code.call(); 100 | } finally { 101 | remove(); 102 | } 103 | } 104 | 105 | @Override 106 | public Strict strict() { 107 | return new Strict<>(name, reader, writer, eraser); 108 | } 109 | 110 | @Override 111 | public Optimized optimized() { 112 | return this; 113 | } 114 | 115 | @Override 116 | public String toString() { 117 | return String.format("[%s]", name); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/var/ModifiableContextVar.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2025 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.var; 17 | 18 | import java.util.Arrays; 19 | import java.util.List; 20 | import java.util.function.Consumer; 21 | import java.util.function.Supplier; 22 | 23 | public interface ModifiableContextVar extends ContextVar { 24 | void set(T value); 25 | 26 | default void remove() { 27 | set(null); 28 | } 29 | 30 | @Override 31 | default ModifiableContextVar strict() { 32 | return this; 33 | } 34 | 35 | @Override 36 | default ModifiableContextVar optimized() { 37 | return this; 38 | } 39 | 40 | public static ModifiableContextVar define(Supplier reader, Consumer writer) { 41 | return define(reader, writer, null); 42 | } 43 | 44 | public static ModifiableContextVar define(String name, Supplier reader, Consumer writer) { 45 | return define(name, reader, writer, null); 46 | } 47 | 48 | public static ModifiableContextVar define(Supplier reader, Consumer writer, Runnable eraser) { 49 | return define(ContextVarHelper.generateVarName(), reader, writer, eraser); 50 | } 51 | 52 | public static ModifiableContextVar define(String name, Supplier reader, Consumer writer, Runnable eraser) { 53 | return new CustomModifiableContextVar.Optimized<>(name, reader, writer, eraser); 54 | } 55 | 56 | @SafeVarargs 57 | public static ContextVar> of(ModifiableContextVar... vars) { 58 | return of(Arrays.asList(vars)); 59 | } 60 | 61 | public static ContextVar> of(List> vars) { 62 | if (null == vars || vars.isEmpty()) { 63 | return ContextVar.empty(); 64 | } 65 | 66 | return new ModifiableContextVarGroup.Optimized(vars); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/var/ModifiableContextVarGroup.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2025 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.var; 17 | 18 | import java.util.List; 19 | import java.util.concurrent.Callable; 20 | import java.util.stream.Collectors; 21 | 22 | abstract class ModifiableContextVarGroup implements ModifiableContextVar> { 23 | final List> vars; 24 | 25 | ModifiableContextVarGroup(List> vars) { 26 | this.vars = vars; 27 | } 28 | 29 | @Override 30 | public List get() { 31 | return vars.stream() 32 | .map(ContextVar::get) 33 | .collect(Collectors.toList()); 34 | } 35 | 36 | @Override 37 | public void set(List value) { 38 | ContextVarHelper.applyValues(vars, value); 39 | } 40 | 41 | @Override 42 | public void remove() { 43 | vars.forEach(ModifiableContextVar::remove); 44 | } 45 | 46 | final static class Strict extends ModifiableContextVarGroup { 47 | 48 | Strict(List> vars) { 49 | // No conversion necessary due to inner logic 50 | super(vars/*ContextVarHelper.convertList(vars, ModifiableContextVar::strict)*/); 51 | } 52 | 53 | @Override 54 | public V callWith(List capturedValue, Callable code) throws Exception { 55 | List previousValues = this.get(); 56 | ContextVarHelper.applyValues(vars, capturedValue); 57 | try { 58 | return code.call(); 59 | } finally { 60 | ContextVarHelper.applyValues(vars, previousValues); 61 | } 62 | } 63 | 64 | @Override 65 | public Strict strict() { 66 | return this; 67 | } 68 | 69 | @Override 70 | public Optimized optimized() { 71 | return new Optimized<>(vars); 72 | } 73 | 74 | @Override 75 | public String toString() { 76 | return String.format("%s", vars); 77 | } 78 | } 79 | 80 | final static class Optimized extends ModifiableContextVarGroup { 81 | 82 | Optimized(List> vars) { 83 | // No conversion necessary due to inner logic 84 | super(vars/*ContextVarHelper.convertList(vars, ModifiableContextVar::optimized)*/); 85 | } 86 | 87 | @Override 88 | public V callWith(List capturedValue, Callable code) throws Exception { 89 | ContextVarHelper.applyValues(vars, capturedValue); 90 | try { 91 | return code.call(); 92 | } finally { 93 | vars.forEach(ModifiableContextVar::remove); 94 | } 95 | } 96 | 97 | @Override 98 | public Strict strict() { 99 | return new Strict<>(vars); 100 | } 101 | 102 | @Override 103 | public Optimized optimized() { 104 | return this; 105 | } 106 | 107 | 108 | @Override 109 | public String toString() { 110 | return String.format("%s", vars); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/net/tascalate/concurrent/var/ThreadLocalVar.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2025 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent.var; 17 | 18 | import java.util.Arrays; 19 | import java.util.List; 20 | import java.util.concurrent.Callable; 21 | 22 | public abstract class ThreadLocalVar implements ModifiableContextVar { 23 | final ThreadLocal delegate; 24 | 25 | ThreadLocalVar(ThreadLocal delegate) { 26 | this.delegate = delegate; 27 | } 28 | 29 | @Override 30 | public T get() { 31 | return delegate.get(); 32 | } 33 | 34 | @Override 35 | public void set(T value) { 36 | delegate.set(value); 37 | } 38 | 39 | @Override 40 | public void remove() { 41 | delegate.remove(); 42 | } 43 | 44 | static final class Strict extends ThreadLocalVar { 45 | 46 | Strict(ThreadLocal delegate) { 47 | super(delegate); 48 | } 49 | 50 | @Override 51 | public V callWith(T capturedValue, Callable code) throws Exception { 52 | T prevValue = this.get(); 53 | try { 54 | return code.call(); 55 | } finally { 56 | if (null == prevValue) { 57 | delegate.remove(); 58 | } else { 59 | delegate.set(prevValue); 60 | } 61 | } 62 | } 63 | 64 | @Override 65 | public Strict strict() { 66 | return this; 67 | } 68 | 69 | @Override 70 | public Optimized optimized() { 71 | return new Optimized<>(delegate); 72 | } 73 | 74 | @Override 75 | public String toString() { 76 | return String.format("[%s]", delegate); 77 | } 78 | 79 | } 80 | 81 | static final class Optimized extends ThreadLocalVar { 82 | 83 | Optimized(ThreadLocal delegate) { 84 | super(delegate); 85 | } 86 | 87 | @Override 88 | public V callWith(T capturedValue, Callable code) throws Exception { 89 | try { 90 | return code.call(); 91 | } finally { 92 | delegate.remove(); 93 | } 94 | } 95 | 96 | @Override 97 | public Strict strict() { 98 | return new Strict<>(delegate); 99 | } 100 | 101 | @Override 102 | public Optimized optimized() { 103 | return this; 104 | } 105 | 106 | @Override 107 | public String toString() { 108 | return String.format("[%s]", delegate); 109 | } 110 | 111 | } 112 | 113 | public static ThreadLocalVar of(ThreadLocal threadLocal) { 114 | return new ThreadLocalVar.Optimized<>(threadLocal); 115 | } 116 | 117 | @SafeVarargs 118 | public static ContextVar> of(ThreadLocal... tls) { 119 | return of(Arrays.asList(tls)); 120 | } 121 | 122 | public static ContextVar> of(List> threadLocals) { 123 | if (null == threadLocals || threadLocals.isEmpty()) { 124 | return ContextVar.empty(); 125 | } else { 126 | return new ThreadLocalVarGroup.Optimized<>(threadLocals); 127 | } 128 | } 129 | 130 | } 131 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Built-By: vsilaev 3 | -------------------------------------------------------------------------------- /src/test/java/net/tascalate/concurrent/EitherCompletableTaskTests.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) - original author Adam Jurčík 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent; 17 | 18 | import java.util.concurrent.CountDownLatch; 19 | import java.util.concurrent.atomic.AtomicInteger; 20 | 21 | import org.junit.After; 22 | import org.junit.Assert; 23 | import org.junit.Before; 24 | import org.junit.Test; 25 | /** 26 | * @author vsilaev 27 | * @author Adam Jurčík 28 | */ 29 | public class EitherCompletableTaskTests { 30 | 31 | private TaskExecutorService executor; 32 | 33 | @Before 34 | public void setup() { 35 | executor = TaskExecutors.newFixedThreadPool(4); 36 | } 37 | 38 | @After 39 | public void tearDown() { 40 | executor.shutdown(); 41 | } 42 | 43 | @Test 44 | public void testAnyTaskIsExecuted1() throws Exception { 45 | CountDownLatch ready = new CountDownLatch(1); 46 | Throwable[] errors = {null, null}; 47 | String[] results = {null, null}; 48 | AtomicInteger idx = new AtomicInteger(0); 49 | 50 | Promise first = executor.submit(() -> resultWithDelay("ABC", 2000)); 51 | Promise second = executor.submit(() -> resultWithDelay("XYZ", 100)); // Second wins -- shorter delay 52 | 53 | first 54 | .applyToEitherAsync(second, String::toLowerCase) 55 | .whenComplete((r, e) -> { 56 | int i = idx.getAndIncrement(); 57 | results[i] = r; 58 | errors[i] = e; 59 | ready.countDown(); 60 | }); 61 | 62 | ready.await(); 63 | Assert.assertNull(errors[0]); 64 | Assert.assertEquals(results[0], "xyz"); 65 | } 66 | 67 | @Test 68 | public void testAnyTaskIsExecuted2() throws Exception { 69 | CountDownLatch ready = new CountDownLatch(1); 70 | Throwable[] errors = {null, null}; 71 | String[] results = {null, null}; 72 | AtomicInteger idx = new AtomicInteger(0); 73 | 74 | Promise first = executor.submit(() -> resultWithDelay("ABC", 2000)); 75 | Promise second = executor.submit(() -> resultWithDelay("XYZ", 100)); // Second wins -- shorter delay 76 | 77 | second 78 | .applyToEitherAsync(first, String::toLowerCase) 79 | .whenComplete((r, e) -> { 80 | int i = idx.getAndIncrement(); 81 | results[i] = r; 82 | errors[i] = e; 83 | ready.countDown(); 84 | }); 85 | 86 | ready.await(); 87 | Assert.assertNull(errors[0]); 88 | Assert.assertEquals(results[0], "xyz"); 89 | } 90 | 91 | 92 | T resultWithDelay(T value, long delay) throws InterruptedException { 93 | Thread.sleep(delay); 94 | return value; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/test/java/net/tascalate/concurrent/Exceptions.java: -------------------------------------------------------------------------------- 1 | package net.tascalate.concurrent; 2 | 3 | import java.util.concurrent.CompletableFuture; 4 | import java.util.concurrent.ExecutionException; 5 | import java.util.concurrent.ThreadFactory; 6 | 7 | import net.tascalate.concurrent.core.CompletionStageAPI; 8 | 9 | public class Exceptions { 10 | public static void main(final String[] argv) throws InterruptedException, ExecutionException { 11 | CompletionStageAPI.current(); 12 | 13 | CompletableFuture stage1 = CompletableFuture.supplyAsync(() -> {throw new IllegalStateException();}); 14 | stage1.whenComplete((r, e) -> { 15 | System.out.println(e); 16 | }); 17 | 18 | CompletableFuture stage2 = CompletableFuture.completedFuture("ABC"); 19 | stage2.thenApply(s -> {throw new RuntimeException("Z");}) 20 | .handle((r, e) -> { 21 | System.out.println(e); 22 | return null; 23 | }); 24 | 25 | final ThreadFactory tf = TaskExecutors.newThreadFactory() 26 | .withNameFormat("CTX-MY-THREAD-%d-OF-%s") 27 | .withThreadGroup( 28 | TaskExecutors.newThreadGroup() 29 | .withName("Tascalate-Tasks") 30 | .withMaxPriority(Thread.NORM_PRIORITY) 31 | .build() 32 | ) 33 | .withContextClassLoader(J8Examples.class.getClassLoader()) 34 | .build(); 35 | 36 | TaskExecutorService executorService = TaskExecutors.newFixedThreadPool(6, tf); 37 | Promise task1 = CompletableTask.completed("ABC", executorService); 38 | task1.thenApply(s -> {throw new RuntimeException("Z");}) 39 | .handle((r, e) -> { 40 | System.out.println(e); 41 | return null; 42 | }); 43 | executorService.shutdownNow(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/net/tascalate/concurrent/InterruptBlockingIO.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent; 17 | 18 | import java.io.Closeable; 19 | import java.io.IOException; 20 | import java.net.HttpURLConnection; 21 | import java.net.URL; 22 | import java.time.Duration; 23 | import java.util.ArrayList; 24 | import java.util.List; 25 | import java.util.concurrent.ExecutorService; 26 | import java.util.concurrent.Executors; 27 | 28 | import net.tascalate.concurrent.io.BlockingIO; 29 | 30 | public class InterruptBlockingIO { 31 | 32 | public static void main(String[] argv) throws Exception { 33 | ExecutorService executors = Executors.newFixedThreadPool(10); 34 | 35 | try { 36 | long startTime = System.currentTimeMillis(); 37 | List> promises = getPromises(executors); 38 | System.out.println("Futures size:" + promises.size()); 39 | Promises.all(false, promises).join(); 40 | System.out.println("All futures are completed in " + (System.currentTimeMillis() - startTime) + "ms"); 41 | } finally { 42 | executors.shutdownNow(); 43 | } 44 | } 45 | 46 | private static List> getPromises(ExecutorService executors) { 47 | List> futures = new ArrayList<>(); 48 | futures.add(get(null, "https://deelay.me/500/https://www.github.com", executors)); // returns within 500ms 49 | futures.add(get(null, "https://deelay.me/40000/https://www.oracle.com", executors)); // returns within 40_000ms (40 seconds) 50 | futures.add(get(null, "https://deelay.me/1100/https://www.google.com", executors)); // returns within 1100ms 51 | return futures; 52 | } 53 | 54 | private static Promise get(Object proxy, String url, ExecutorService executors) { 55 | return CompletableTask.supplyAsync(BlockingIO.interruptible(() -> callHttp(proxy, url)), executors) 56 | .onTimeout(() -> { 57 | System.out.println("Timeout with url " + url); 58 | return defaultResponse(); 59 | }, Duration.ofSeconds(5), true) 60 | .thenAccept(BlockingIO.interruptible(v -> {})) 61 | .whenComplete(BlockingIO.interruptible((r, e) -> {})) 62 | .thenApply(__ -> ""); 63 | } 64 | 65 | private static String callHttp(Object proxy, String url) { 66 | System.out.println("Calling " + url + "..."); 67 | Object content; 68 | try { 69 | HttpURLConnection conn = (HttpURLConnection)(new URL(url).openConnection()); 70 | try (Closeable resource = BlockingIO.register(conn::disconnect)) { 71 | content = conn.getContent(); 72 | System.out.println("Called " + url); 73 | } 74 | } catch (IOException ex) { 75 | System.out.println("ERROR reading " + url + " ==> " + ex.getLocalizedMessage()); 76 | content = null; 77 | } 78 | 79 | return content == null ? null : content.toString(); 80 | } 81 | 82 | private static String defaultResponse() { 83 | return "Timeout!!!"; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/test/java/net/tascalate/concurrent/LookupCancelMethod.java: -------------------------------------------------------------------------------- 1 | package net.tascalate.concurrent; 2 | 3 | import java.util.concurrent.CompletableFuture; 4 | 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import net.tascalate.concurrent.core.CancelMethodsCache; 9 | 10 | public class LookupCancelMethod { 11 | 12 | @Before 13 | public void setUp() { 14 | 15 | } 16 | 17 | @Test 18 | public void testRegularCancleMethod() { 19 | CompletableFuture o = CompletableFuture.completedFuture("ABC"); 20 | CancelMethodsCache.cancellationOf(o.getClass()).apply(o, false); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/net/tascalate/concurrent/OrTimeoutExceptionTests.java: -------------------------------------------------------------------------------- 1 | package net.tascalate.concurrent; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import java.time.Duration; 6 | import java.util.concurrent.CompletionException; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | import org.junit.After; 10 | import org.junit.Before; 11 | import org.junit.Test; 12 | 13 | public class OrTimeoutExceptionTests { 14 | 15 | private TaskExecutorService executor; 16 | 17 | @Before 18 | public void setUp() { 19 | executor = TaskExecutors.newFixedThreadPool(4); 20 | } 21 | 22 | @After 23 | public void tearDown() { 24 | executor.shutdown(); 25 | } 26 | 27 | @Test 28 | public void testExceptionalOrTimeout() { 29 | Promise p = 30 | CompletableTask 31 | .submit(this::doTask, executor) 32 | .orTimeout(Duration.ofSeconds(5L)) 33 | .whenComplete((res, err) -> { 34 | if (res != null) 35 | System.out.println("RESULT: " + res); 36 | else 37 | System.out.println("ERROR: " + err.getMessage()); 38 | }); 39 | try { 40 | p.join(); 41 | } catch (CompletionException ex) { 42 | assertTrue(ex.getCause() instanceof IllegalStateException); 43 | } 44 | } 45 | 46 | private String doTask () throws Exception { 47 | TimeUnit.SECONDS.sleep(3); 48 | if (null != System.out) 49 | throw new IllegalStateException("my error"); 50 | return "executed ok"; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/net/tascalate/concurrent/PromisesTests.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent; 17 | 18 | import static org.junit.Assert.assertTrue; 19 | 20 | import java.math.BigDecimal; 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | 24 | import org.junit.After; 25 | import org.junit.Before; 26 | import org.junit.Test; 27 | 28 | public class PromisesTests { 29 | 30 | @Before 31 | public void setUp() { 32 | } 33 | 34 | @After 35 | public void tearDown() { 36 | } 37 | 38 | @Test 39 | public void testGenericArgsWithList() { 40 | Promise> p = combineWithList(BigDecimal.valueOf(10), Long.valueOf(11)); 41 | List v = p.join(); 42 | assertTrue("Fisrt is BigDecimal ", v.get(0) instanceof BigDecimal); 43 | assertTrue("Second is Long ", v.get(1) instanceof Long); 44 | } 45 | 46 | @Test 47 | public void testGenericArgsWithArray() { 48 | Promise> p = combineWithArray1(BigDecimal.valueOf(10), Long.valueOf(11)); 49 | List v = p.join(); 50 | assertTrue("Fisrt is BigDecimal ", v.get(0) instanceof BigDecimal); 51 | assertTrue("Second is Long ", v.get(1) instanceof Long); 52 | } 53 | 54 | 55 | Promise> combineWithList(U a, V b) { 56 | List> promises = new ArrayList<>(); 57 | promises.add(Promises.success(a)); 58 | promises.add(Promises.success(b)); 59 | Promise> all = Promises.all(promises); 60 | return all; 61 | } 62 | 63 | Promise> combineWithArray1(U a, V b) { 64 | @SuppressWarnings("unchecked") 65 | Promise[] promises = new Promise[2]; 66 | promises[0] = Promises.success(a); 67 | promises[1] = Promises.success(b); 68 | Promise> all = Promises.all(promises); 69 | return all; 70 | } 71 | 72 | Promise> combineWithArray2(U a, V b) { 73 | Promise> all = Promises.all(Promises.success(a), Promises.success(b)); 74 | return all; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/test/java/net/tascalate/concurrent/SimpleCompletableTaskTests.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent; 17 | 18 | import java.util.concurrent.CompletionException; 19 | import java.util.concurrent.CountDownLatch; 20 | 21 | import org.junit.After; 22 | import org.junit.Assert; 23 | import org.junit.Before; 24 | import org.junit.Test; 25 | 26 | public class SimpleCompletableTaskTests { 27 | 28 | private TaskExecutorService executor; 29 | 30 | @Before 31 | public void setup() { 32 | executor = TaskExecutors.newFixedThreadPool(4); 33 | } 34 | 35 | @After 36 | public void tearDown() { 37 | executor.shutdown(); 38 | } 39 | 40 | @Test 41 | public void testTaskIsExecuted1() throws Exception { 42 | Promise promise = executor.submit(() -> 50); 43 | int i = promise.get(); 44 | Assert.assertEquals(i, 50); 45 | } 46 | 47 | @Test 48 | public void testTaskIsExecuted2() throws Exception { 49 | CountDownLatch ready = new CountDownLatch(1); 50 | Throwable[] errors = {null}; 51 | String[] results = {null}; 52 | executor.submit(() -> "ABC").whenComplete((r, e) -> { 53 | results[0] = r; 54 | errors[0] = e; 55 | ready.countDown(); 56 | }); 57 | ready.await(); 58 | Assert.assertNull(errors[0]); 59 | Assert.assertEquals(results[0], "ABC"); 60 | } 61 | 62 | @Test 63 | public void testTaskIsFailed() throws Exception { 64 | CountDownLatch ready = new CountDownLatch(1); 65 | Throwable[] errors = {null}; 66 | Integer[] results = {null}; 67 | executor.submit(this::failedResult).whenComplete((r, e) -> { 68 | results[0] = r; 69 | errors[0] = e; 70 | ready.countDown(); 71 | }); 72 | ready.await(); 73 | Assert.assertNull(results[0]); 74 | Assert.assertTrue("ArithmeticException was not raised", errors[0] instanceof ArithmeticException); 75 | } 76 | 77 | @Test 78 | public void testNestedTaskIsExecuted() throws Exception { 79 | CountDownLatch ready = new CountDownLatch(1); 80 | Throwable[] errors = {null}; 81 | Integer[] results = {null}; 82 | executor.submit(() -> 50).thenApply(i -> i * 10).whenComplete((r, e) -> { 83 | results[0] = r; 84 | errors[0] = e; 85 | ready.countDown(); 86 | }); 87 | ready.await(); 88 | Assert.assertNull(errors[0]); 89 | Assert.assertEquals(results[0], Integer.valueOf(500)); 90 | } 91 | 92 | @Test 93 | public void testNestedTaskIsFailed() throws Exception { 94 | CountDownLatch ready = new CountDownLatch(1); 95 | Throwable[] errors = {null}; 96 | Integer[] results = {null}; 97 | executor.submit(this::failedResult).thenApply(i -> i * 10).whenComplete((r, e) -> { 98 | results[0] = r; 99 | errors[0] = e; 100 | ready.countDown(); 101 | }); 102 | ready.await(); 103 | Assert.assertNull(results[0]); 104 | Assert.assertTrue("CompletionException was not raised", errors[0] instanceof CompletionException); 105 | Assert.assertTrue("CompletionException is not caused by ArithmeticException", errors[0].getCause() instanceof ArithmeticException); 106 | } 107 | 108 | 109 | int failedResult() { 110 | throw new ArithmeticException(); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/test/java/net/tascalate/concurrent/StopExecutorTests.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent; 17 | 18 | import static org.junit.Assert.assertFalse; 19 | import static org.junit.Assert.assertTrue; 20 | 21 | import java.util.concurrent.CountDownLatch; 22 | import java.util.concurrent.ExecutionException; 23 | import java.util.concurrent.ExecutorService; 24 | import java.util.concurrent.Executors; 25 | import java.util.concurrent.atomic.AtomicBoolean; 26 | 27 | import org.junit.After; 28 | import org.junit.Before; 29 | import org.junit.Test; 30 | 31 | /** 32 | * @author vsilaev 33 | */ 34 | public class StopExecutorTests { 35 | 36 | private TaskExecutorService executor; 37 | 38 | @Before 39 | public void setUp() { 40 | executor = TaskExecutors.newFixedThreadPool(4); 41 | } 42 | 43 | @After 44 | public void tearDown() { 45 | executor.shutdown(); 46 | } 47 | 48 | @Test 49 | public void test_shutting_down_executor_will_not_hang_promise_due_to_pending_callback() throws ExecutionException, InterruptedException { 50 | ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); 51 | 52 | CountDownLatch latch = new CountDownLatch(1); 53 | AtomicBoolean flag = new AtomicBoolean(); 54 | Promise p = CompletableTask.asyncOn(executor) 55 | .thenApplyAsync(v -> { 56 | try { 57 | latch.await(); 58 | } catch (InterruptedException ex) { 59 | } 60 | return v; 61 | }, singleThreadExecutor) 62 | .handleAsync((v, t) -> { 63 | flag.set(true); 64 | return null; 65 | }); 66 | 67 | singleThreadExecutor.shutdownNow(); 68 | latch.countDown(); 69 | //singleThreadExecutor.awaitTermination(10, TimeUnit.SECONDS); 70 | try { 71 | p.get(); 72 | assertFalse("Promise should not be resolved susscessfully", true); 73 | } catch (Exception ex) { 74 | assertTrue("Exception is thrown: " + ex.getMessage(), true); 75 | } 76 | assertFalse(flag.get()); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/test/java/net/tascalate/concurrent/TestMultitargetExceptionTraces.java: -------------------------------------------------------------------------------- 1 | package net.tascalate.concurrent; 2 | 3 | import java.io.IOException; 4 | import java.util.Arrays; 5 | 6 | public class TestMultitargetExceptionTraces { 7 | 8 | 9 | public static void main(String[] argv) { 10 | MultitargetException e = (MultitargetException)err(); 11 | //Throwable e = outer().initCause(err()); 12 | //e.printStackTrace(new PrintWriter(System.err, true)); 13 | e.printStackTrace(); 14 | System.err.println("--------------"); 15 | System.err.println(e.getLocalizedMessage()); 16 | System.err.println("--------------"); 17 | e.printExceptions(); 18 | } 19 | 20 | static Exception outer() { 21 | return new IOException("Invalid user input"); 22 | } 23 | 24 | static Exception err() { 25 | return err_1(); 26 | } 27 | 28 | static Exception err_1() { 29 | MultitargetException e = new MultitargetException("First message", Arrays.asList( 30 | null, 31 | null, 32 | b(), null, 33 | a(), null, 34 | new MultitargetException("Another message", Arrays.asList(c(), b())) 35 | )); 36 | //e.fillInStackTrace(); 37 | return e; 38 | } 39 | 40 | static Throwable a() { 41 | Exception e = new IllegalArgumentException("Something wrong", b()); 42 | //e.fillInStackTrace(); 43 | return e; 44 | } 45 | 46 | static Throwable b() { 47 | return b_1(); 48 | } 49 | 50 | static Throwable b_1() { 51 | return b_2(); 52 | } 53 | 54 | static Throwable b_2() { 55 | Throwable e = new NoSuchMethodError("Data not found"); 56 | //e.fillInStackTrace(); 57 | return e; 58 | } 59 | 60 | static Throwable c() { 61 | return c_1(); 62 | } 63 | 64 | static Throwable c_1() { 65 | return c_2(); 66 | } 67 | 68 | static Exception c_2() { 69 | Exception e = new IllegalStateException("State is forbidden"); 70 | //e.fillInStackTrace(); 71 | return e; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/test/java/net/tascalate/concurrent/ThenComposeAsyncTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) - original author Adam Jurčík 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent; 17 | 18 | import static org.junit.Assert.fail; 19 | 20 | import java.util.concurrent.ExecutionException; 21 | import java.util.concurrent.TimeUnit; 22 | import java.util.concurrent.TimeoutException; 23 | import java.util.concurrent.atomic.AtomicBoolean; 24 | import org.junit.After; 25 | import org.junit.Assert; 26 | import org.junit.Before; 27 | import org.junit.Test; 28 | 29 | /** 30 | * @author vsilaev 31 | * @author Adam Jurčík 32 | */ 33 | public class ThenComposeAsyncTest { 34 | 35 | private TaskExecutorService executor; 36 | 37 | @Before 38 | public void setUp() { 39 | executor = TaskExecutors.newFixedThreadPool(4); 40 | } 41 | 42 | @After 43 | public void tearDown() { 44 | executor.shutdown(); 45 | } 46 | 47 | @Test 48 | public void testThenComposeAsyncRace() { 49 | AtomicBoolean ran = new AtomicBoolean(false); 50 | AtomicBoolean started = new AtomicBoolean(false); 51 | AtomicBoolean cancelled = new AtomicBoolean(false); 52 | 53 | Promise p = CompletableTask.completed(10, executor) 54 | .thenComposeAsync(n -> executor.submit(() -> { 55 | started.set(true); 56 | try { 57 | Thread.sleep(200); 58 | } catch (InterruptedException ex) { 59 | // cancelled 60 | cancelled.set(true); 61 | } 62 | return 10 * n; 63 | })); 64 | 65 | @SuppressWarnings("unused") 66 | Promise pThen = p.thenRunAsync(() -> { 67 | ran.set(true); 68 | }); 69 | 70 | // Wait some time in order to hit the DELAY in after fn.apply(r) 71 | trySleep(50); 72 | p.cancel(true); 73 | trySleep(500); 74 | 75 | // Ok, since p is completed only once in onError(...) through cancel(...) 76 | Assert.assertFalse("Expected ran false, but is " + ran.get(), ran.get()); 77 | // Ok, since task is created quickly in thenComposeAsync(...) 78 | Assert.assertTrue("Expected started true, but is " + started.get(), started.get()); 79 | // Fails, since there is a race between completion stage creation 80 | // and setting it as a cancellableOrigin 81 | Assert.assertTrue("Expected cancelled true, but is " + cancelled.get(), cancelled.get()); 82 | } 83 | 84 | 85 | @Test 86 | public void testThenComposeException() { 87 | Promise p = CompletableTask.runAsync(() -> trySleep(10), executor) 88 | .thenCompose(it -> { 89 | throw new IllegalStateException("oh no!"); 90 | }); 91 | try { 92 | p.get(1L, TimeUnit.SECONDS); 93 | } catch (TimeoutException ex) { 94 | fail("thenCompose* hanged"); 95 | } catch (InterruptedException | ExecutionException ex) { 96 | // System.out.println(ex); 97 | // expected 98 | return; 99 | } 100 | fail("Exception must be thrown"); 101 | } 102 | 103 | 104 | private void trySleep(long millis) { 105 | try { 106 | Thread.sleep(millis); 107 | } catch (InterruptedException ex) { 108 | } 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /src/test/java/net/tascalate/concurrent/TimeoutDifferentExecutors.java: -------------------------------------------------------------------------------- 1 | package net.tascalate.concurrent; 2 | 3 | import java.time.Duration; 4 | import java.util.concurrent.CompletionException; 5 | import java.util.concurrent.Executors; 6 | 7 | public class TimeoutDifferentExecutors { 8 | 9 | public static void main(String[] argv) { 10 | String v = 11 | CompletableTask 12 | .supplyAsync(() -> { 13 | try { 14 | Thread.sleep(10_000); // sleep 10s 15 | } catch (InterruptedException e) { 16 | System.err.println("Interrupted"); 17 | throw new CompletionException(e); 18 | } 19 | System.out.println("Interrupted? " + (Thread.currentThread().isInterrupted() ? "yes" : "no")); 20 | return "foo"; 21 | }, Executors.newFixedThreadPool(1)) 22 | .orTimeout(Duration.ofMillis(200), true) 23 | .join(); 24 | System.out.println(v); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/net/tascalate/concurrent/WhenCompleteAsyncTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2021 Valery Silaev (http://vsilaev.com) - original author Adam Jurčík 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.tascalate.concurrent; 17 | 18 | import java.util.concurrent.CompletionException; 19 | import java.util.concurrent.ExecutionException; 20 | import java.util.concurrent.ForkJoinPool; 21 | import org.hamcrest.CoreMatchers; 22 | import org.junit.Assert; 23 | import org.junit.Test; 24 | 25 | /** 26 | * @author Adam Jurčík 27 | */ 28 | public class WhenCompleteAsyncTest { 29 | 30 | @Test 31 | public void testSuppressedSelfAddition() throws InterruptedException, ExecutionException { 32 | Promise failure = CompletableTask 33 | .asyncOn(ForkJoinPool.commonPool()) 34 | .thenRunAsync( 35 | () -> { 36 | throw new CompletionException(new Exception()); 37 | }) 38 | .whenCompleteAsync((r, ex) -> { /* no action */ }) 39 | .handleAsync((r, ex) -> ex); 40 | 41 | Throwable t = failure.get(); 42 | 43 | Assert.assertThat(t, CoreMatchers.instanceOf(CompletionException.class)); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/net/tascalate/concurrent/delays/NanosTest.java: -------------------------------------------------------------------------------- 1 | package net.tascalate.concurrent.delays; 2 | 3 | import java.time.Duration; 4 | import java.util.Iterator; 5 | import java.util.Random; 6 | import java.util.concurrent.CompletionStage; 7 | 8 | @SuppressWarnings("unused") 9 | public class NanosTest { 10 | 11 | public static void main(String[] args) { 12 | 13 | // Duration dd = Duration.ofMillis(Long.MAX_VALUE); 14 | System.out.println(Long.MAX_VALUE); 15 | System.out.println(-Long.MIN_VALUE); 16 | System.out.println(Long.MIN_VALUE); 17 | System.out.println(-Long.MAX_VALUE); 18 | System.out.println("--REF-- = " + Long.MAX_VALUE); 19 | // System.out.println("NANOS = " + DurationCalcs.safeExtractAmount(dd, 0)); 20 | // System.out.println("MICROS = " + DurationCalcs.safeExtractAmount(dd, 1)); 21 | // System.out.println("MILLIS = " + DurationCalcs.safeExtractAmount(dd, 2)); 22 | // System.out.println("SECONDS = " + DurationCalcs.safeExtractAmount(dd, 3)); 23 | // 24 | // System.out.println(Duration.ofMillis(Long.MAX_VALUE).getSeconds()); 25 | // System.out.println("++"); 26 | Duration d = Duration.ofSeconds((long)(Long.MAX_VALUE * 0.95)); 27 | System.out.println(d.getSeconds()); 28 | System.out.println(random1(d).getSeconds()); 29 | System.out.println(random1(d).getSeconds()); 30 | System.out.println(random1(d).getSeconds()); 31 | System.out.println(random1(d).getSeconds()); 32 | System.out.println(random1(d).getSeconds()); 33 | System.out.println(random1(d).getSeconds()); 34 | } 35 | 36 | private static Duration exp(Duration initialDelay, double multiplier, int pow) { 37 | double factor = Math.pow(multiplier, pow); 38 | return DurationCalcs.safeTransform( 39 | initialDelay, 40 | (amount, dimIdx) -> Long.MAX_VALUE / amount > factor ? 1 : 0, 41 | (amount, dimIdx) -> (long)(amount * factor) 42 | ); 43 | } 44 | 45 | private static Duration random1(Duration initialDelay) { 46 | double randomizer = random.nextDouble(); 47 | return DurationCalcs.safeTransform( 48 | initialDelay, 49 | (amount, dimIdx) -> checkBounds1(amount, randomizer) ? 1 : 0, 50 | (amount, dimIdx) -> addRandomJitter1(amount, randomizer) 51 | ); 52 | } 53 | 54 | private static Duration random2(Duration initialDelay, double multiplier, int pow) { 55 | double randomizer = random.nextDouble(); 56 | return DurationCalcs.safeTransform( 57 | initialDelay, 58 | (amount, dimIdx) -> checkBounds1(amount, randomizer) ? 1 : 0, 59 | (amount, dimIdx) -> addRandomJitter1(amount, randomizer) 60 | ); 61 | } 62 | 63 | 64 | static final Random random = new Random(); 65 | 66 | 67 | static long addRandomJitter1(long initialDelay, double randomizer) { 68 | double randomMultiplier = (1 - 2 * randomizer) * 0.1; 69 | System.out.println("::MULT:: " + (long)(initialDelay * (1 + randomMultiplier))); 70 | return Math.max(0, (long) (initialDelay * (1 + randomMultiplier))); 71 | } 72 | 73 | static boolean checkBounds1(long initialDelay, double randomizer) { 74 | double randomMultiplier = (1 - 2 * randomizer) * 0.1; 75 | System.out.println("::MULT:: " + (long)(initialDelay * (1 + randomMultiplier))); 76 | return (double)Long.MAX_VALUE / initialDelay > Math.abs(randomMultiplier + 1); 77 | } 78 | 79 | /* 80 | if (initialDelay > 0) { 81 | if (uniformRandom > 0) 82 | return Long.MAX_VALUE - initialDelay > uniformRandom; //+MAX > +A + +B 83 | else 84 | return Long.MIN_VALUE + initialDelay < uniformRandom; //-MIN < -A - (+B) 85 | } else { 86 | if (uniformRandom < 0) 87 | return Long.MIN_VALUE - initialDelay < uniformRandom; //-MIN < -A + (-B) 88 | else 89 | return Long.MAX_VALUE + initialDelay > uniformRandom; //+MAX > +A - (-B) 90 | } 91 | 92 | */ 93 | } 94 | --------------------------------------------------------------------------------