PaperMC/patches/server/0821-fixup-ConcurrentUtil.patch
Spottedleaf ecf4d9715e Begin fixing issues
See diff in the update text file
2024-10-25 11:25:09 -07:00

6177 lines
229 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Mon, 21 Oct 2024 11:47:34 -0700
Subject: [PATCH] fixup! ConcurrentUtil
diff --git a/src/main/java/ca/spottedleaf/concurrentutil/collection/SRSWLinkedQueue.java b/src/main/java/ca/spottedleaf/concurrentutil/collection/SRSWLinkedQueue.java
deleted file mode 100644
index 094eff418b4e3bffce020d650931b4d9e58fa9ed..0000000000000000000000000000000000000000
--- a/src/main/java/ca/spottedleaf/concurrentutil/collection/SRSWLinkedQueue.java
+++ /dev/null
@@ -1,149 +0,0 @@
-package ca.spottedleaf.concurrentutil.collection;
-
-import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
-import ca.spottedleaf.concurrentutil.util.Validate;
-
-import java.lang.invoke.VarHandle;
-import java.util.ConcurrentModificationException;
-
-/**
- * Single reader thread single writer thread queue. The reader side of the queue is ordered by acquire semantics,
- * and the writer side of the queue is ordered by release semantics.
- */
-// TODO test
-public class SRSWLinkedQueue<E> {
-
- // always non-null
- protected LinkedNode<E> head;
-
- // always non-null
- protected LinkedNode<E> tail;
-
- /* IMPL NOTE: Leave hashCode and equals to their defaults */
-
- public SRSWLinkedQueue() {
- final LinkedNode<E> dummy = new LinkedNode<>(null, null);
- this.head = this.tail = dummy;
- }
-
- /**
- * Must be the reader thread.
- *
- * <p>
- * Returns, without removing, the first element of this queue.
- * </p>
- * @return Returns, without removing, the first element of this queue.
- */
- public E peekFirst() {
- LinkedNode<E> head = this.head;
- E ret = head.getElementPlain();
- if (ret == null) {
- head = head.getNextAcquire();
- if (head == null) {
- // empty
- return null;
- }
- // update head reference for next poll() call
- this.head = head;
- // guaranteed to be non-null
- ret = head.getElementPlain();
- if (ret == null) {
- throw new ConcurrentModificationException("Multiple reader threads");
- }
- }
-
- return ret;
- }
-
- /**
- * Must be the reader thread.
- *
- * <p>
- * Returns and removes the first element of this queue.
- * </p>
- * @return Returns and removes the first element of this queue.
- */
- public E poll() {
- LinkedNode<E> head = this.head;
- E ret = head.getElementPlain();
- if (ret == null) {
- head = head.getNextAcquire();
- if (head == null) {
- // empty
- return null;
- }
- // guaranteed to be non-null
- ret = head.getElementPlain();
- if (ret == null) {
- throw new ConcurrentModificationException("Multiple reader threads");
- }
- }
-
- head.setElementPlain(null);
- LinkedNode<E> next = head.getNextAcquire();
- this.head = next == null ? head : next;
-
- return ret;
- }
-
- /**
- * Must be the writer thread.
- *
- * <p>
- * Adds the element to the end of the queue.
- * </p>
- *
- * @throws NullPointerException If the provided element is null
- */
- public void addLast(final E element) {
- Validate.notNull(element, "Provided element cannot be null");
- final LinkedNode<E> append = new LinkedNode<>(element, null);
-
- this.tail.setNextRelease(append);
- this.tail = append;
- }
-
- protected static final class LinkedNode<E> {
-
- protected volatile Object element;
- protected volatile LinkedNode<E> next;
-
- protected static final VarHandle ELEMENT_HANDLE = ConcurrentUtil.getVarHandle(LinkedNode.class, "element", Object.class);
- protected static final VarHandle NEXT_HANDLE = ConcurrentUtil.getVarHandle(LinkedNode.class, "next", LinkedNode.class);
-
- protected LinkedNode(final Object element, final LinkedNode<E> next) {
- ELEMENT_HANDLE.set(this, element);
- NEXT_HANDLE.set(this, next);
- }
-
- /* element */
-
- @SuppressWarnings("unchecked")
- protected final E getElementPlain() {
- return (E)ELEMENT_HANDLE.get(this);
- }
-
- protected final void setElementPlain(final E update) {
- ELEMENT_HANDLE.set(this, (Object)update);
- }
- /* next */
-
- @SuppressWarnings("unchecked")
- protected final LinkedNode<E> getNextPlain() {
- return (LinkedNode<E>)NEXT_HANDLE.get(this);
- }
-
- @SuppressWarnings("unchecked")
- protected final LinkedNode<E> getNextAcquire() {
- return (LinkedNode<E>)NEXT_HANDLE.getAcquire(this);
- }
-
- protected final void setNextPlain(final LinkedNode<E> next) {
- NEXT_HANDLE.set(this, next);
- }
-
- protected final void setNextRelease(final LinkedNode<E> next) {
- NEXT_HANDLE.setRelease(this, next);
- }
- }
-}
diff --git a/src/main/java/ca/spottedleaf/concurrentutil/completable/CallbackCompletable.java b/src/main/java/ca/spottedleaf/concurrentutil/completable/CallbackCompletable.java
new file mode 100644
index 0000000000000000000000000000000000000000..6bad6f8ecc0944d2f406924c7de7e227ff1e70fa
--- /dev/null
+++ b/src/main/java/ca/spottedleaf/concurrentutil/completable/CallbackCompletable.java
@@ -0,0 +1,110 @@
+package ca.spottedleaf.concurrentutil.completable;
+
+import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue;
+import ca.spottedleaf.concurrentutil.executor.Cancellable;
+import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.util.function.BiConsumer;
+
+public final class CallbackCompletable<T> {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(CallbackCompletable.class);
+
+ private final MultiThreadedQueue<BiConsumer<T, Throwable>> waiters = new MultiThreadedQueue<>();
+ private T result;
+ private Throwable throwable;
+ private volatile boolean completed;
+
+ public boolean isCompleted() {
+ return this.completed;
+ }
+
+ /**
+ * Note: Can only use after calling {@link #addAsynchronousWaiter(BiConsumer)}, as this function performs zero
+ * synchronisation
+ */
+ public T getResult() {
+ return this.result;
+ }
+
+ /**
+ * Note: Can only use after calling {@link #addAsynchronousWaiter(BiConsumer)}, as this function performs zero
+ * synchronisation
+ */
+ public Throwable getThrowable() {
+ return this.throwable;
+ }
+
+ /**
+ * Adds a waiter that should only be completed asynchronously by the complete() calls. If complete()
+ * has already been called, returns {@code null} and does not invoke the specified consumer.
+ * @param consumer Consumer to be executed on completion
+ * @throws NullPointerException If consumer is null
+ * @return A cancellable which will control the execution of the specified consumer
+ */
+ public Cancellable addAsynchronousWaiter(final BiConsumer<T, Throwable> consumer) {
+ if (this.waiters.add(consumer)) {
+ return new CancellableImpl(consumer);
+ }
+ return null;
+ }
+
+ private void completeAllWaiters(final T result, final Throwable throwable) {
+ this.completed = true;
+ BiConsumer<T, Throwable> waiter;
+ while ((waiter = this.waiters.pollOrBlockAdds()) != null) {
+ this.completeWaiter(waiter, result, throwable);
+ }
+ }
+
+ private void completeWaiter(final BiConsumer<T, Throwable> consumer, final T result, final Throwable throwable) {
+ try {
+ consumer.accept(result, throwable);
+ } catch (final Throwable throwable2) {
+ LOGGER.error("Failed to complete callback " + ConcurrentUtil.genericToString(consumer), throwable2);
+ }
+ }
+
+ /**
+ * Adds a waiter that will be completed asynchronously by the complete() calls. If complete()
+ * has already been called, then invokes the consumer synchronously with the completed result.
+ * @param consumer Consumer to be executed on completion
+ * @throws NullPointerException If consumer is null
+ * @return A cancellable which will control the execution of the specified consumer
+ */
+ public Cancellable addWaiter(final BiConsumer<T, Throwable> consumer) {
+ if (this.waiters.add(consumer)) {
+ return new CancellableImpl(consumer);
+ }
+ this.completeWaiter(consumer, this.result, this.throwable);
+ return new CancellableImpl(consumer);
+ }
+
+ public void complete(final T result) {
+ this.result = result;
+ this.completeAllWaiters(result, null);
+ }
+
+ public void completeWithThrowable(final Throwable throwable) {
+ if (throwable == null) {
+ throw new NullPointerException("Throwable cannot be null");
+ }
+ this.throwable = throwable;
+ this.completeAllWaiters(null, throwable);
+ }
+
+ private final class CancellableImpl implements Cancellable {
+
+ private final BiConsumer<T, Throwable> waiter;
+
+ private CancellableImpl(final BiConsumer<T, Throwable> waiter) {
+ this.waiter = waiter;
+ }
+
+ @Override
+ public boolean cancel() {
+ return CallbackCompletable.this.waiters.remove(this.waiter);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ca/spottedleaf/concurrentutil/completable/Completable.java b/src/main/java/ca/spottedleaf/concurrentutil/completable/Completable.java
index 46d1bd01542ebeeffc0006a5c585a50dbbbff907..365616439fa079017d648ed7f6ddf6950a691adf 100644
--- a/src/main/java/ca/spottedleaf/concurrentutil/completable/Completable.java
+++ b/src/main/java/ca/spottedleaf/concurrentutil/completable/Completable.java
@@ -1,112 +1,737 @@
package ca.spottedleaf.concurrentutil.completable;
-import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue;
-import ca.spottedleaf.concurrentutil.executor.Cancellable;
import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
+import ca.spottedleaf.concurrentutil.util.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.lang.invoke.VarHandle;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.locks.LockSupport;
import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
public final class Completable<T> {
private static final Logger LOGGER = LoggerFactory.getLogger(Completable.class);
+ private static final Function<? super Throwable, ? extends Throwable> DEFAULT_EXCEPTION_HANDLER = (final Throwable thr) -> {
+ LOGGER.error("Unhandled exception during Completable operation", thr);
+ return thr;
+ };
- private final MultiThreadedQueue<BiConsumer<T, Throwable>> waiters = new MultiThreadedQueue<>();
- private T result;
- private Throwable throwable;
- private volatile boolean completed;
+ public static Executor getDefaultExecutor() {
+ return ForkJoinPool.commonPool();
+ }
+
+ private static final Transform<?, ?> COMPLETED_STACK = new Transform<>(null, null, null, null) {
+ @Override
+ public void run() {}
+ };
+ private volatile Transform<?, T> completeStack;
+ private static final VarHandle COMPLETE_STACK_HANDLE = ConcurrentUtil.getVarHandle(Completable.class, "completeStack", Transform.class);
+
+ private static final Object NULL_MASK = new Object();
+ private volatile Object result;
+ private static final VarHandle RESULT_HANDLE = ConcurrentUtil.getVarHandle(Completable.class, "result", Object.class);
- public boolean isCompleted() {
- return this.completed;
+ private Object getResultPlain() {
+ return (Object)RESULT_HANDLE.get(this);
}
- /**
- * Note: Can only use after calling {@link #addAsynchronousWaiter(BiConsumer)}, as this function performs zero
- * synchronisation
- */
- public T getResult() {
- return this.result;
+ private Object getResultVolatile() {
+ return (Object)RESULT_HANDLE.getVolatile(this);
}
- /**
- * Note: Can only use after calling {@link #addAsynchronousWaiter(BiConsumer)}, as this function performs zero
- * synchronisation
- */
- public Throwable getThrowable() {
- return this.throwable;
+ private void pushStackOrRun(final Transform<?, T> push) {
+ int failures = 0;
+ for (Transform<?, T> curr = (Transform<?, T>)COMPLETE_STACK_HANDLE.getVolatile(this);;) {
+ if (curr == COMPLETED_STACK) {
+ push.execute();
+ return;
+ }
+
+ push.next = curr;
+
+ for (int i = 0; i < failures; ++i) {
+ ConcurrentUtil.backoff();
+ }
+
+ if (curr == (curr = (Transform<?, T>)COMPLETE_STACK_HANDLE.compareAndExchange(this, curr, push))) {
+ return;
+ }
+ push.next = null;
+ ++failures;
+ }
}
- /**
- * Adds a waiter that should only be completed asynchronously by the complete() calls. If complete()
- * has already been called, returns {@code null} and does not invoke the specified consumer.
- * @param consumer Consumer to be executed on completion
- * @throws NullPointerException If consumer is null
- * @return A cancellable which will control the execution of the specified consumer
- */
- public Cancellable addAsynchronousWaiter(final BiConsumer<T, Throwable> consumer) {
- if (this.waiters.add(consumer)) {
- return new CancellableImpl(consumer);
+ private void propagateStack() {
+ Transform<?, T> topStack = (Transform<?, T>)COMPLETE_STACK_HANDLE.getAndSet(this, COMPLETED_STACK);
+ while (topStack != null) {
+ topStack.execute();
+ topStack = topStack.next;
}
- return null;
}
- private void completeAllWaiters(final T result, final Throwable throwable) {
- this.completed = true;
- BiConsumer<T, Throwable> waiter;
- while ((waiter = this.waiters.pollOrBlockAdds()) != null) {
- this.completeWaiter(waiter, result, throwable);
+ private static Object maskNull(final Object res) {
+ return res == null ? NULL_MASK : res;
+ }
+
+ private static Object unmaskNull(final Object res) {
+ return res == NULL_MASK ? null : res;
+ }
+
+ private static Executor checkExecutor(final Executor executor) {
+ return Validate.notNull(executor, "Executor may not be null");
+ }
+
+ public Completable() {}
+
+ private Completable(final Object complete) {
+ COMPLETE_STACK_HANDLE.set(this, COMPLETED_STACK);
+ RESULT_HANDLE.setRelease(this, complete);
+ }
+
+ public static <T> Completable<T> completed(final T value) {
+ return new Completable<>(maskNull(value));
+ }
+
+ public static <T> Completable<T> failed(final Throwable ex) {
+ Validate.notNull(ex, "Exception may not be null");
+
+ return new Completable<>(new ExceptionResult(ex));
+ }
+
+ public static <T> Completable<T> supplied(final Supplier<T> supplier) {
+ return supplied(supplier, DEFAULT_EXCEPTION_HANDLER);
+ }
+
+ public static <T> Completable<T> supplied(final Supplier<T> supplier, final Function<? super Throwable, ? extends Throwable> exceptionHandler) {
+ try {
+ return completed(supplier.get());
+ } catch (final Throwable throwable) {
+ Throwable complete;
+ try {
+ complete = exceptionHandler.apply(throwable);
+ } catch (final Throwable thr2) {
+ throwable.addSuppressed(thr2);
+ complete = throwable;
+ }
+ return failed(complete);
}
}
- private void completeWaiter(final BiConsumer<T, Throwable> consumer, final T result, final Throwable throwable) {
+ public static <T> Completable<T> suppliedAsync(final Supplier<T> supplier, final Executor executor) {
+ return suppliedAsync(supplier, executor, DEFAULT_EXCEPTION_HANDLER);
+ }
+
+ public static <T> Completable<T> suppliedAsync(final Supplier<T> supplier, final Executor executor, final Function<? super Throwable, ? extends Throwable> exceptionHandler) {
+ final Completable<T> ret = new Completable<>();
+
+ class AsyncSuppliedCompletable implements Runnable, CompletableFuture.AsynchronousCompletionTask {
+ @Override
+ public void run() {
+ try {
+ ret.complete(supplier.get());
+ } catch (final Throwable throwable) {
+ Throwable complete;
+ try {
+ complete = exceptionHandler.apply(throwable);
+ } catch (final Throwable thr2) {
+ throwable.addSuppressed(thr2);
+ complete = throwable;
+ }
+ ret.completeExceptionally(complete);
+ }
+ }
+ }
+
try {
- consumer.accept(result, throwable);
- } catch (final ThreadDeath death) {
- throw death;
- } catch (final Throwable throwable2) {
- LOGGER.error("Failed to complete callback " + ConcurrentUtil.genericToString(consumer), throwable2);
+ executor.execute(new AsyncSuppliedCompletable());
+ } catch (final Throwable throwable) {
+ Throwable complete;
+ try {
+ complete = exceptionHandler.apply(throwable);
+ } catch (final Throwable thr2) {
+ throwable.addSuppressed(thr2);
+ complete = throwable;
+ }
+ ret.completeExceptionally(complete);
+ }
+
+ return ret;
+ }
+
+ private boolean completeRaw(final Object value) {
+ if ((Object)RESULT_HANDLE.getVolatile(this) != null || !(boolean)RESULT_HANDLE.compareAndSet(this, (Object)null, value)) {
+ return false;
+ }
+
+ this.propagateStack();
+ return true;
+ }
+
+ public boolean complete(final T result) {
+ return this.completeRaw(maskNull(result));
+ }
+
+ public boolean completeExceptionally(final Throwable exception) {
+ Validate.notNull(exception, "Exception may not be null");
+
+ return this.completeRaw(new ExceptionResult(exception));
+ }
+
+ public boolean isDone() {
+ return this.getResultVolatile() != null;
+ }
+
+ public boolean isNormallyComplete() {
+ return this.getResultVolatile() != null && !(this.getResultVolatile() instanceof ExceptionResult);
+ }
+
+ public boolean isExceptionallyComplete() {
+ return this.getResultVolatile() instanceof ExceptionResult;
+ }
+
+ public Throwable getException() {
+ final Object res = this.getResultVolatile();
+ if (res == null) {
+ return null;
+ }
+
+ if (!(res instanceof ExceptionResult exRes)) {
+ throw new IllegalStateException("Not completed exceptionally");
+ }
+
+ return exRes.ex;
+ }
+
+ public T getNow(final T dfl) throws CompletionException {
+ final Object res = this.getResultVolatile();
+ if (res == null) {
+ return dfl;
+ }
+
+ if (res instanceof ExceptionResult exRes) {
+ throw new CompletionException(exRes.ex);
+ }
+
+ return (T)unmaskNull(res);
+ }
+
+ public T join() throws CompletionException {
+ if (this.isDone()) {
+ return this.getNow(null);
+ }
+
+ final UnparkTransform<T> unparkTransform = new UnparkTransform<>(this, Thread.currentThread());
+
+ this.pushStackOrRun(unparkTransform);
+
+ boolean interuptted = false;
+ while (!unparkTransform.isReleasable()) {
+ try {
+ ForkJoinPool.managedBlock(unparkTransform);
+ } catch (final InterruptedException ex) {
+ interuptted = true;
+ }
+ }
+
+ if (interuptted) {
+ Thread.currentThread().interrupt();
+ }
+
+ return this.getNow(null);
+ }
+
+ public CompletableFuture<T> toFuture() {
+ final Object rawResult = this.getResultVolatile();
+ if (rawResult != null) {
+ if (rawResult instanceof ExceptionResult exRes) {
+ return CompletableFuture.failedFuture(exRes.ex);
+ } else {
+ return CompletableFuture.completedFuture((T)unmaskNull(rawResult));
+ }
+ }
+
+ final CompletableFuture<T> ret = new CompletableFuture<>();
+
+ class ToFuture implements BiConsumer<T, Throwable> {
+
+ @Override
+ public void accept(final T res, final Throwable ex) {
+ if (ex != null) {
+ ret.completeExceptionally(ex);
+ } else {
+ ret.complete(res);
+ }
+ }
+ }
+
+ this.whenComplete(new ToFuture());
+
+ return ret;
+ }
+
+ public static <T> Completable<T> fromFuture(final CompletionStage<T> stage) {
+ final Completable<T> ret = new Completable<>();
+
+ class FromFuture implements BiConsumer<T, Throwable> {
+ @Override
+ public void accept(final T res, final Throwable ex) {
+ if (ex != null) {
+ ret.completeExceptionally(ex);
+ } else {
+ ret.complete(res);
+ }
+ }
+ }
+
+ stage.whenComplete(new FromFuture());
+
+ return ret;
+ }
+
+
+ public <U> Completable<U> thenApply(final Function<? super T, ? extends U> function) {
+ return this.thenApply(function, DEFAULT_EXCEPTION_HANDLER);
+ }
+
+ public <U> Completable<U> thenApply(final Function<? super T, ? extends U> function, final Function<? super Throwable, ? extends Throwable> exceptionHandler) {
+ Validate.notNull(function, "Function may not be null");
+ Validate.notNull(exceptionHandler, "Exception handler may not be null");
+
+ final Completable<U> ret = new Completable<>();
+ this.pushStackOrRun(new ApplyTransform<>(null, this, ret, exceptionHandler, function));
+ return ret;
+ }
+
+ public <U> Completable<U> thenApplyAsync(final Function<? super T, ? extends U> function) {
+ return this.thenApplyAsync(function, getDefaultExecutor(), DEFAULT_EXCEPTION_HANDLER);
+ }
+
+ public <U> Completable<U> thenApplyAsync(final Function<? super T, ? extends U> function, final Executor executor) {
+ return this.thenApplyAsync(function, executor, DEFAULT_EXCEPTION_HANDLER);
+ }
+
+ public <U> Completable<U> thenApplyAsync(final Function<? super T, ? extends U> function, final Executor executor, final Function<? super Throwable, ? extends Throwable> exceptionHandler) {
+ Validate.notNull(function, "Function may not be null");
+ Validate.notNull(exceptionHandler, "Exception handler may not be null");
+
+ final Completable<U> ret = new Completable<>();
+ this.pushStackOrRun(new ApplyTransform<>(checkExecutor(executor), this, ret, exceptionHandler, function));
+ return ret;
+ }
+
+
+ public Completable<Void> thenAccept(final Consumer<? super T> consumer) {
+ return this.thenAccept(consumer, DEFAULT_EXCEPTION_HANDLER);
+ }
+
+ public Completable<Void> thenAccept(final Consumer<? super T> consumer, final Function<? super Throwable, ? extends Throwable> exceptionHandler) {
+ Validate.notNull(consumer, "Consumer may not be null");
+ Validate.notNull(exceptionHandler, "Exception handler may not be null");
+
+ final Completable<Void> ret = new Completable<>();
+ this.pushStackOrRun(new AcceptTransform<>(null, this, ret, exceptionHandler, consumer));
+ return ret;
+ }
+
+ public Completable<Void> thenAcceptAsync(final Consumer<? super T> consumer) {
+ return this.thenAcceptAsync(consumer, getDefaultExecutor(), DEFAULT_EXCEPTION_HANDLER);
+ }
+
+ public Completable<Void> thenAcceptAsync(final Consumer<? super T> consumer, final Executor executor) {
+ return this.thenAcceptAsync(consumer, executor, DEFAULT_EXCEPTION_HANDLER);
+ }
+
+ public Completable<Void> thenAcceptAsync(final Consumer<? super T> consumer, final Executor executor, final Function<? super Throwable, ? extends Throwable> exceptionHandler) {
+ Validate.notNull(consumer, "Consumer may not be null");
+ Validate.notNull(exceptionHandler, "Exception handler may not be null");
+
+ final Completable<Void> ret = new Completable<>();
+ this.pushStackOrRun(new AcceptTransform<>(checkExecutor(executor), this, ret, exceptionHandler, consumer));
+ return ret;
+ }
+
+
+ public Completable<Void> thenRun(final Runnable run) {
+ return this.thenRun(run, DEFAULT_EXCEPTION_HANDLER);
+ }
+
+ public Completable<Void> thenRun(final Runnable run, final Function<? super Throwable, ? extends Throwable> exceptionHandler) {
+ Validate.notNull(run, "Run may not be null");
+ Validate.notNull(exceptionHandler, "Exception handler may not be null");
+
+ final Completable<Void> ret = new Completable<>();
+ this.pushStackOrRun(new RunTransform<>(null, this, ret, exceptionHandler, run));
+ return ret;
+ }
+
+ public Completable<Void> thenRunAsync(final Runnable run) {
+ return this.thenRunAsync(run, getDefaultExecutor(), DEFAULT_EXCEPTION_HANDLER);
+ }
+
+ public Completable<Void> thenRunAsync(final Runnable run, final Executor executor) {
+ return this.thenRunAsync(run, executor, DEFAULT_EXCEPTION_HANDLER);
+ }
+
+ public Completable<Void> thenRunAsync(final Runnable run, final Executor executor, final Function<? super Throwable, ? extends Throwable> exceptionHandler) {
+ Validate.notNull(run, "Run may not be null");
+ Validate.notNull(exceptionHandler, "Exception handler may not be null");
+
+ final Completable<Void> ret = new Completable<>();
+ this.pushStackOrRun(new RunTransform<>(checkExecutor(executor), this, ret, exceptionHandler, run));
+ return ret;
+ }
+
+
+ public <U> Completable<U> handle(final BiFunction<? super T, ? super Throwable, ? extends U> function) {
+ return this.handle(function, DEFAULT_EXCEPTION_HANDLER);
+ }
+
+ public <U> Completable<U> handle(final BiFunction<? super T, ? super Throwable, ? extends U> function,
+ final Function<? super Throwable, ? extends Throwable> exceptionHandler) {
+ Validate.notNull(function, "Function may not be null");
+ Validate.notNull(exceptionHandler, "Exception handler may not be null");
+
+ final Completable<U> ret = new Completable<>();
+ this.pushStackOrRun(new HandleTransform<>(null, this, ret, exceptionHandler, function));
+ return ret;
+ }
+
+ public <U> Completable<U> handleAsync(final BiFunction<? super T, ? super Throwable, ? extends U> function) {
+ return this.handleAsync(function, getDefaultExecutor(), DEFAULT_EXCEPTION_HANDLER);
+ }
+
+ public <U> Completable<U> handleAsync(final BiFunction<? super T, ? super Throwable, ? extends U> function,
+ final Executor executor) {
+ return this.handleAsync(function, executor, DEFAULT_EXCEPTION_HANDLER);
+ }
+
+ public <U> Completable<U> handleAsync(final BiFunction<? super T, ? super Throwable, ? extends U> function,
+ final Executor executor,
+ final Function<? super Throwable, ? extends Throwable> exceptionHandler) {
+ Validate.notNull(function, "Function may not be null");
+ Validate.notNull(exceptionHandler, "Exception handler may not be null");
+
+ final Completable<U> ret = new Completable<>();
+ this.pushStackOrRun(new HandleTransform<>(checkExecutor(executor), this, ret, exceptionHandler, function));
+ return ret;
+ }
+
+
+ public Completable<T> whenComplete(final BiConsumer<? super T, ? super Throwable> consumer) {
+ return this.whenComplete(consumer, DEFAULT_EXCEPTION_HANDLER);
+ }
+
+ public Completable<T> whenComplete(final BiConsumer<? super T, ? super Throwable> consumer, final Function<? super Throwable, ? extends Throwable> exceptionHandler) {
+ Validate.notNull(consumer, "Consumer may not be null");
+ Validate.notNull(exceptionHandler, "Exception handler may not be null");
+
+ final Completable<T> ret = new Completable<>();
+ this.pushStackOrRun(new WhenTransform<>(null, this, ret, exceptionHandler, consumer));
+ return ret;
+ }
+
+ public Completable<T> whenCompleteAsync(final BiConsumer<? super T, ? super Throwable> consumer) {
+ return this.whenCompleteAsync(consumer, getDefaultExecutor(), DEFAULT_EXCEPTION_HANDLER);
+ }
+
+ public Completable<T> whenCompleteAsync(final BiConsumer<? super T, ? super Throwable> consumer, final Executor executor) {
+ return this.whenCompleteAsync(consumer, executor, DEFAULT_EXCEPTION_HANDLER);
+ }
+
+ public Completable<T> whenCompleteAsync(final BiConsumer<? super T, ? super Throwable> consumer, final Executor executor,
+ final Function<? super Throwable, ? extends Throwable> exceptionHandler) {
+ Validate.notNull(consumer, "Consumer may not be null");
+ Validate.notNull(exceptionHandler, "Exception handler may not be null");
+
+ final Completable<T> ret = new Completable<>();
+ this.pushStackOrRun(new WhenTransform<>(checkExecutor(executor), this, ret, exceptionHandler, consumer));
+ return ret;
+ }
+
+
+ public Completable<T> exceptionally(final Function<Throwable, ? extends T> function) {
+ return this.exceptionally(function, DEFAULT_EXCEPTION_HANDLER);
+ }
+
+ public Completable<T> exceptionally(final Function<Throwable, ? extends T> function, final Function<? super Throwable, ? extends Throwable> exceptionHandler) {
+ Validate.notNull(function, "Function may not be null");
+ Validate.notNull(exceptionHandler, "Exception handler may not be null");
+
+ final Completable<T> ret = new Completable<>();
+ this.pushStackOrRun(new ExceptionallyTransform<>(null, this, ret, exceptionHandler, function));
+ return ret;
+ }
+
+ public Completable<T> exceptionallyAsync(final Function<Throwable, ? extends T> function) {
+ return this.exceptionallyAsync(function, getDefaultExecutor(), DEFAULT_EXCEPTION_HANDLER);
+ }
+
+ public Completable<T> exceptionallyAsync(final Function<Throwable, ? extends T> function, final Executor executor) {
+ return this.exceptionallyAsync(function, executor, DEFAULT_EXCEPTION_HANDLER);
+ }
+
+ public Completable<T> exceptionallyAsync(final Function<Throwable, ? extends T> function, final Executor executor,
+ final Function<? super Throwable, ? extends Throwable> exceptionHandler) {
+ Validate.notNull(function, "Function may not be null");
+ Validate.notNull(exceptionHandler, "Exception handler may not be null");
+
+ final Completable<T> ret = new Completable<>();
+ this.pushStackOrRun(new ExceptionallyTransform<>(checkExecutor(executor), this, ret, exceptionHandler, function));
+ return ret;
+ }
+
+ private static final class ExceptionResult {
+ public final Throwable ex;
+
+ public ExceptionResult(final Throwable ex) {
+ this.ex = ex;
}
}
- /**
- * Adds a waiter that will be completed asynchronously by the complete() calls. If complete()
- * has already been called, then invokes the consumer synchronously with the completed result.
- * @param consumer Consumer to be executed on completion
- * @throws NullPointerException If consumer is null
- * @return A cancellable which will control the execution of the specified consumer
- */
- public Cancellable addWaiter(final BiConsumer<T, Throwable> consumer) {
- if (this.waiters.add(consumer)) {
- return new CancellableImpl(consumer);
+ private static abstract class Transform<U, T> implements Runnable, CompletableFuture.AsynchronousCompletionTask {
+
+ private Transform<?, T> next;
+
+ private final Executor executor;
+ protected final Completable<T> from;
+ protected final Completable<U> to;
+ protected final Function<? super Throwable, ? extends Throwable> exceptionHandler;
+
+ protected Transform(final Executor executor, final Completable<T> from, final Completable<U> to,
+ final Function<? super Throwable, ? extends Throwable> exceptionHandler) {
+ this.executor = executor;
+ this.from = from;
+ this.to = to;
+ this.exceptionHandler = exceptionHandler;
+ }
+
+ // force interface call to become virtual call
+ @Override
+ public abstract void run();
+
+ protected void failed(final Throwable throwable) {
+ Throwable complete;
+ try {
+ complete = this.exceptionHandler.apply(throwable);
+ } catch (final Throwable thr2) {
+ throwable.addSuppressed(thr2);
+ complete = throwable;
+ }
+ this.to.completeExceptionally(complete);
+ }
+
+ public void execute() {
+ if (this.executor == null) {
+ this.run();
+ return;
+ }
+
+ try {
+ this.executor.execute(this);
+ } catch (final Throwable throwable) {
+ this.failed(throwable);
+ }
+ }
+ }
+
+ private static final class ApplyTransform<U, T> extends Transform<U, T> {
+
+ private final Function<? super T, ? extends U> function;
+
+ public ApplyTransform(final Executor executor, final Completable<T> from, final Completable<U> to,
+ final Function<? super Throwable, ? extends Throwable> exceptionHandler,
+ final Function<? super T, ? extends U> function) {
+ super(executor, from, to, exceptionHandler);
+ this.function = function;
+ }
+
+ @Override
+ public void run() {
+ final Object result = this.from.getResultPlain();
+ try {
+ if (result instanceof ExceptionResult exRes) {
+ this.to.completeExceptionally(exRes.ex);
+ } else {
+ this.to.complete(this.function.apply((T)unmaskNull(result)));
+ }
+ } catch (final Throwable throwable) {
+ this.failed(throwable);
+ }
}
- this.completeWaiter(consumer, this.result, this.throwable);
- return new CancellableImpl(consumer);
}
- public void complete(final T result) {
- this.result = result;
- this.completeAllWaiters(result, null);
+ private static final class AcceptTransform<T> extends Transform<Void, T> {
+ private final Consumer<? super T> consumer;
+
+ public AcceptTransform(final Executor executor, final Completable<T> from, final Completable<Void> to,
+ final Function<? super Throwable, ? extends Throwable> exceptionHandler,
+ final Consumer<? super T> consumer) {
+ super(executor, from, to, exceptionHandler);
+ this.consumer = consumer;
+ }
+
+ @Override
+ public void run() {
+ final Object result = this.from.getResultPlain();
+ try {
+ if (result instanceof ExceptionResult exRes) {
+ this.to.completeExceptionally(exRes.ex);
+ } else {
+ this.consumer.accept((T)unmaskNull(result));
+ this.to.complete(null);
+ }
+ } catch (final Throwable throwable) {
+ this.failed(throwable);
+ }
+ }
}
- public void completeWithThrowable(final Throwable throwable) {
- if (throwable == null) {
- throw new NullPointerException("Throwable cannot be null");
+ private static final class RunTransform<T> extends Transform<Void, T> {
+ private final Runnable run;
+
+ public RunTransform(final Executor executor, final Completable<T> from, final Completable<Void> to,
+ final Function<? super Throwable, ? extends Throwable> exceptionHandler,
+ final Runnable run) {
+ super(executor, from, to, exceptionHandler);
+ this.run = run;
+ }
+
+ @Override
+ public void run() {
+ final Object result = this.from.getResultPlain();
+ try {
+ if (result instanceof ExceptionResult exRes) {
+ this.to.completeExceptionally(exRes.ex);
+ } else {
+ this.run.run();
+ this.to.complete(null);
+ }
+ } catch (final Throwable throwable) {
+ this.failed(throwable);
+ }
}
- this.throwable = throwable;
- this.completeAllWaiters(null, throwable);
}
- private final class CancellableImpl implements Cancellable {
+ private static final class HandleTransform<U, T> extends Transform<U, T> {
+
+ private final BiFunction<? super T, ? super Throwable, ? extends U> function;
+
+ public HandleTransform(final Executor executor, final Completable<T> from, final Completable<U> to,
+ final Function<? super Throwable, ? extends Throwable> exceptionHandler,
+ final BiFunction<? super T, ? super Throwable, ? extends U> function) {
+ super(executor, from, to, exceptionHandler);
+ this.function = function;
+ }
- private final BiConsumer<T, Throwable> waiter;
+ @Override
+ public void run() {
+ final Object result = this.from.getResultPlain();
+ try {
+ if (result instanceof ExceptionResult exRes) {
+ this.to.complete(this.function.apply(null, exRes.ex));
+ } else {
+ this.to.complete(this.function.apply((T)unmaskNull(result), null));
+ }
+ } catch (final Throwable throwable) {
+ this.failed(throwable);
+ }
+ }
+ }
+
+ private static final class WhenTransform<T> extends Transform<T, T> {
+
+ private final BiConsumer<? super T, ? super Throwable> consumer;
+
+ public WhenTransform(final Executor executor, final Completable<T> from, final Completable<T> to,
+ final Function<? super Throwable, ? extends Throwable> exceptionHandler,
+ final BiConsumer<? super T, ? super Throwable> consumer) {
+ super(executor, from, to, exceptionHandler);
+ this.consumer = consumer;
+ }
+
+ @Override
+ public void run() {
+ final Object result = this.from.getResultPlain();
+ try {
+ if (result instanceof ExceptionResult exRes) {
+ this.consumer.accept(null, exRes.ex);
+ this.to.completeExceptionally(exRes.ex);
+ } else {
+ final T unmasked = (T)unmaskNull(result);
+ this.consumer.accept(unmasked, null);
+ this.to.complete(unmasked);
+ }
+ } catch (final Throwable throwable) {
+ this.failed(throwable);
+ }
+ }
+ }
+
+ private static final class ExceptionallyTransform<T> extends Transform<T, T> {
+ private final Function<Throwable, ? extends T> function;
+
+ public ExceptionallyTransform(final Executor executor, final Completable<T> from, final Completable<T> to,
+ final Function<? super Throwable, ? extends Throwable> exceptionHandler,
+ final Function<Throwable, ? extends T> function) {
+ super(executor, from, to, exceptionHandler);
+ this.function = function;
+ }
+
+ @Override
+ public void run() {
+ final Object result = this.from.getResultPlain();
+ try {
+ if (result instanceof ExceptionResult exRes) {
+ this.to.complete(this.function.apply(exRes.ex));
+ } else {
+ this.to.complete((T)unmaskNull(result));
+ }
+ } catch (final Throwable throwable) {
+ this.failed(throwable);
+ }
+ }
+ }
+
+ private static final class UnparkTransform<T> extends Transform<Void, T> implements ForkJoinPool.ManagedBlocker {
+
+ private volatile Thread thread;
+
+ public UnparkTransform(final Completable<T> from, final Thread target) {
+ super(null, from, null, null);
+ this.thread = target;
+ }
+
+ @Override
+ public void run() {
+ final Thread t = this.thread;
+ this.thread = null;
+ LockSupport.unpark(t);
+ }
+
+ @Override
+ public boolean block() throws InterruptedException {
+ while (!this.isReleasable()) {
+ if (Thread.interrupted()) {
+ throw new InterruptedException();
+ }
+ LockSupport.park(this);
+ }
- private CancellableImpl(final BiConsumer<T, Throwable> waiter) {
- this.waiter = waiter;
+ return true;
}
@Override
- public boolean cancel() {
- return Completable.this.waiters.remove(this.waiter);
+ public boolean isReleasable() {
+ return this.thread == null;
}
}
}
diff --git a/src/main/java/ca/spottedleaf/concurrentutil/executor/BaseExecutor.java b/src/main/java/ca/spottedleaf/concurrentutil/executor/BaseExecutor.java
deleted file mode 100644
index 18d646676fd022afd64afaac30ec1bd283a73b0e..0000000000000000000000000000000000000000
--- a/src/main/java/ca/spottedleaf/concurrentutil/executor/BaseExecutor.java
+++ /dev/null
@@ -1,208 +0,0 @@
-package ca.spottedleaf.concurrentutil.executor;
-
-import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
-import java.util.function.BooleanSupplier;
-
-/**
- * Base implementation for an abstract queue of tasks which are executed either synchronously or asynchronously.
- *
- * <p>
- * The implementation supports tracking task executions using {@link #getTotalTasksScheduled()} and
- * {@link #getTotalTasksExecuted()}, and optionally shutting down the executor using {@link #shutdown()}
- * </p>
- *
- * <p>
- * The base implementation does not provide a method to queue a task for execution, rather that is specified in
- * the specific implementation. However, it is required that a specific implementation provides a method to
- * <i>queue</i> a task or <i>create</i> a task. A <i>queued</i> task is one which will eventually be executed,
- * and a <i>created</i> task must be queued to execute via {@link BaseTask#queue()} or be executed manually via
- * {@link BaseTask#execute()}. This choice of delaying the queueing of a task may be useful to provide a task handle
- * which may be cancelled or adjusted before the actual real task logic is ready to be executed.
- * </p>
- */
-public interface BaseExecutor {
-
- /**
- * Returns whether every task scheduled to this queue has been removed and executed or cancelled. If no tasks have been queued,
- * returns {@code true}.
- *
- * @return {@code true} if all tasks that have been queued have finished executing or no tasks have been queued, {@code false} otherwise.
- */
- public default boolean haveAllTasksExecuted() {
- // order is important
- // if new tasks are scheduled between the reading of these variables, scheduled is guaranteed to be higher -
- // so our check fails, and we try again
- final long completed = this.getTotalTasksExecuted();
- final long scheduled = this.getTotalTasksScheduled();
-
- return completed == scheduled;
- }
-
- /**
- * Returns the number of tasks that have been scheduled or execute or are pending to be scheduled.
- */
- public long getTotalTasksScheduled();
-
- /**
- * Returns the number of tasks that have fully been executed.
- */
- public long getTotalTasksExecuted();
-
- /**
- * Waits until this queue has had all of its tasks executed (NOT removed). See {@link #haveAllTasksExecuted()}
- * <p>
- * This call is most effective after a {@link #shutdown()} call, as the shutdown call guarantees no tasks can
- * be executed and the waitUntilAllExecuted call makes sure the queue is empty. Effectively, using shutdown then using
- * waitUntilAllExecuted ensures this queue is empty - and most importantly, will remain empty.
- * </p>
- * <p>
- * This method is not guaranteed to be immediately responsive to queue state, so calls may take significantly more
- * time than expected. Effectively, do not rely on this call being fast - even if there are few tasks scheduled.
- * </p>
- * <p>
- * Note: Interruptions to the the current thread have no effect. Interrupt status is also not affected by this call.
- * </p>
- *
- * @throws IllegalStateException If the current thread is not allowed to wait
- */
- public default void waitUntilAllExecuted() throws IllegalStateException {
- long failures = 1L; // start at 0.25ms
-
- while (!this.haveAllTasksExecuted()) {
- Thread.yield();
- failures = ConcurrentUtil.linearLongBackoff(failures, 250_000L, 5_000_000L); // 500us, 5ms
- }
- }
-
- /**
- * Executes the next available task.
- *
- * @return {@code true} if a task was executed, {@code false} otherwise
- * @throws IllegalStateException If the current thread is not allowed to execute a task
- */
- public boolean executeTask() throws IllegalStateException;
-
- /**
- * Executes all queued tasks.
- *
- * @return {@code true} if a task was executed, {@code false} otherwise
- * @throws IllegalStateException If the current thread is not allowed to execute a task
- */
- public default boolean executeAll() {
- if (!this.executeTask()) {
- return false;
- }
-
- while (this.executeTask());
-
- return true;
- }
-
- /**
- * Waits and executes tasks until the condition returns {@code true}.
- * <p>
- * WARNING: This function is <i>not</i> suitable for waiting until a deadline!
- * Use {@link #executeUntil(long)} or {@link #executeConditionally(BooleanSupplier, long)} instead.
- * </p>
- */
- public default void executeConditionally(final BooleanSupplier condition) {
- long failures = 0;
- while (!condition.getAsBoolean()) {
- if (this.executeTask()) {
- failures = failures >>> 2;
- } else {
- failures = ConcurrentUtil.linearLongBackoff(failures, 100_000L, 10_000_000L); // 100us, 10ms
- }
- }
- }
-
- /**
- * Waits and executes tasks until the condition returns {@code true} or {@code System.nanoTime() - deadline >= 0}.
- */
- public default void executeConditionally(final BooleanSupplier condition, final long deadline) {
- long failures = 0;
- // double check deadline; we don't know how expensive the condition is
- while ((System.nanoTime() - deadline < 0L) && !condition.getAsBoolean() && (System.nanoTime() - deadline < 0L)) {
- if (this.executeTask()) {
- failures = failures >>> 2;
- } else {
- failures = ConcurrentUtil.linearLongBackoffDeadline(failures, 100_000L, 10_000_000L, deadline); // 100us, 10ms
- }
- }
- }
-
- /**
- * Waits and executes tasks until {@code System.nanoTime() - deadline >= 0}.
- */
- public default void executeUntil(final long deadline) {
- long failures = 0;
- while (System.nanoTime() - deadline < 0L) {
- if (this.executeTask()) {
- failures = failures >>> 2;
- } else {
- failures = ConcurrentUtil.linearLongBackoffDeadline(failures, 100_000L, 10_000_000L, deadline); // 100us, 10ms
- }
- }
- }
-
- /**
- * Prevent further additions to this queue. Attempts to add after this call has completed (potentially during) will
- * result in {@link IllegalStateException} being thrown.
- * <p>
- * This operation is atomic with respect to other shutdown calls
- * </p>
- * <p>
- * After this call has completed, regardless of return value, this queue will be shutdown.
- * </p>
- *
- * @return {@code true} if the queue was shutdown, {@code false} if it has shut down already
- * @throws UnsupportedOperationException If this queue does not support shutdown
- * @see #isShutdown()
- */
- public default boolean shutdown() throws UnsupportedOperationException {
- throw new UnsupportedOperationException();
- }
-
- /**
- * Returns whether this queue has shut down. Effectively, whether new tasks will be rejected - this method
- * does not indicate whether all the tasks scheduled have been executed.
- * @return Returns whether this queue has shut down.
- * @see #waitUntilAllExecuted()
- */
- public default boolean isShutdown() {
- return false;
- }
-
- /**
- * Task object returned for any {@link BaseExecutor} scheduled task.
- * @see BaseExecutor
- */
- public static interface BaseTask extends Cancellable {
-
- /**
- * Causes a lazily queued task to become queued or executed
- *
- * @throws IllegalStateException If the backing queue has shutdown
- * @return {@code true} If the task was queued, {@code false} if the task was already queued/cancelled/executed
- */
- public boolean queue();
-
- /**
- * Forces this task to be marked as completed.
- *
- * @return {@code true} if the task was cancelled, {@code false} if the task has already completed or is being completed.
- */
- @Override
- public boolean cancel();
-
- /**
- * Executes this task. This will also mark the task as completing.
- * <p>
- * Exceptions thrown from the runnable will be rethrown.
- * </p>
- *
- * @return {@code true} if this task was executed, {@code false} if it was already marked as completed.
- */
- public boolean execute();
- }
-}
diff --git a/src/main/java/ca/spottedleaf/concurrentutil/executor/PrioritisedExecutor.java b/src/main/java/ca/spottedleaf/concurrentutil/executor/PrioritisedExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..17cbaee1e89bd3f6d905e640d20d0119ab0570a0
--- /dev/null
+++ b/src/main/java/ca/spottedleaf/concurrentutil/executor/PrioritisedExecutor.java
@@ -0,0 +1,271 @@
+package ca.spottedleaf.concurrentutil.executor;
+
+import ca.spottedleaf.concurrentutil.util.Priority;
+
+public interface PrioritisedExecutor {
+
+ /**
+ * Returns the number of tasks that have been scheduled are pending to be scheduled.
+ */
+ public long getTotalTasksScheduled();
+
+ /**
+ * Returns the number of tasks that have been executed.
+ */
+ public long getTotalTasksExecuted();
+
+ /**
+ * Generates the next suborder id.
+ * @return The next suborder id.
+ */
+ public long generateNextSubOrder();
+
+ /**
+ * Executes the next available task.
+ * <p>
+ * If there is a task with priority {@link Priority#BLOCKING} available, then that such task is executed.
+ * </p>
+ * <p>
+ * If there is a task with priority {@link Priority#IDLE} available then that task is only executed
+ * when there are no other tasks available with a higher priority.
+ * </p>
+ * <p>
+ * If there are no tasks that have priority {@link Priority#BLOCKING} or {@link Priority#IDLE}, then
+ * this function will be biased to execute tasks that have higher priorities.
+ * </p>
+ *
+ * @return {@code true} if a task was executed, {@code false} otherwise
+ * @throws IllegalStateException If the current thread is not allowed to execute a task
+ */
+ public boolean executeTask() throws IllegalStateException;
+
+ /**
+ * Prevent further additions to this executor. Attempts to add after this call has completed (potentially during) will
+ * result in {@link IllegalStateException} being thrown.
+ * <p>
+ * This operation is atomic with respect to other shutdown calls
+ * </p>
+ * <p>
+ * After this call has completed, regardless of return value, this executor will be shutdown.
+ * </p>
+ *
+ * @return {@code true} if the executor was shutdown, {@code false} if it has shut down already
+ * @see #isShutdown()
+ */
+ public boolean shutdown();
+
+ /**
+ * Returns whether this executor has shut down. Effectively, returns whether new tasks will be rejected.
+ * This method does not indicate whether all the tasks scheduled have been executed.
+ * @return Returns whether this executor has shut down.
+ */
+ public boolean isShutdown();
+
+ /**
+ * Queues or executes a task at {@link Priority#NORMAL} priority.
+ * @param task The task to run.
+ *
+ * @throws IllegalStateException If this executor has shutdown.
+ * @throws NullPointerException If the task is null
+ * @return {@code null} if the current thread immediately executed the task, else returns the prioritised task
+ * associated with the parameter
+ */
+ public PrioritisedTask queueTask(final Runnable task);
+
+ /**
+ * Queues or executes a task.
+ *
+ * @param task The task to run.
+ * @param priority The priority for the task.
+ *
+ * @throws IllegalStateException If this executor has shutdown.
+ * @throws NullPointerException If the task is null
+ * @throws IllegalArgumentException If the priority is invalid.
+ * @return {@code null} if the current thread immediately executed the task, else returns the prioritised task
+ * associated with the parameter
+ */
+ public PrioritisedTask queueTask(final Runnable task, final Priority priority);
+
+ /**
+ * Queues or executes a task.
+ *
+ * @param task The task to run.
+ * @param priority The priority for the task.
+ * @param subOrder The task's suborder.
+ *
+ * @throws IllegalStateException If this executor has shutdown.
+ * @throws NullPointerException If the task is null
+ * @throws IllegalArgumentException If the priority is invalid.
+ * @return {@code null} if the current thread immediately executed the task, else returns the prioritised task
+ * associated with the parameter
+ */
+ public PrioritisedTask queueTask(final Runnable task, final Priority priority, final long subOrder);
+
+ /**
+ * Creates, but does not queue or execute, a task at {@link Priority#NORMAL} priority.
+ * @param task The task to run.
+ *
+ * @throws NullPointerException If the task is null
+ * @return {@code null} if the current thread immediately executed the task, else returns the prioritised task
+ * associated with the parameter
+ */
+ public PrioritisedTask createTask(final Runnable task);
+
+ /**
+ * Creates, but does not queue or execute, a task at {@link Priority#NORMAL} priority.
+ *
+ * @param task The task to run.
+ * @param priority The priority for the task.
+ *
+ * @throws NullPointerException If the task is null
+ * @throws IllegalArgumentException If the priority is invalid.
+ * @return {@code null} if the current thread immediately executed the task, else returns the prioritised task
+ * associated with the parameter
+ */
+ public PrioritisedTask createTask(final Runnable task, final Priority priority);
+
+ /**
+ * Creates, but does not queue or execute, a task at {@link Priority#NORMAL} priority.
+ *
+ * @param task The task to run.
+ * @param priority The priority for the task.
+ * @param subOrder The task's suborder.
+ *
+ * @throws NullPointerException If the task is null
+ * @throws IllegalArgumentException If the priority is invalid.
+ * @return {@code null} if the current thread immediately executed the task, else returns the prioritised task
+ * associated with the parameter
+ */
+ public PrioritisedTask createTask(final Runnable task, final Priority priority, final long subOrder);
+
+ public static interface PrioritisedTask extends Cancellable {
+
+ /**
+ * Returns the executor associated with this task.
+ * @return The executor associated with this task.
+ */
+ public PrioritisedExecutor getExecutor();
+
+ /**
+ * Causes a lazily queued task to become queued or executed
+ *
+ * @throws IllegalStateException If the backing executor has shutdown
+ * @return {@code true} If the task was queued, {@code false} if the task was already queued/cancelled/executed
+ */
+ public boolean queue();
+
+ /**
+ * Returns whether this task has been queued and is not completing.
+ * @return {@code true} If the task has been queued, {@code false} if the task has not been queued or is marked
+ * as completing.
+ */
+ public boolean isQueued();
+
+ /**
+ * Forces this task to be marked as completed.
+ *
+ * @return {@code true} if the task was cancelled, {@code false} if the task has already completed
+ * or is being completed.
+ */
+ @Override
+ public boolean cancel();
+
+ /**
+ * Executes this task. This will also mark the task as completing.
+ * <p>
+ * Exceptions thrown from the runnable will be rethrown.
+ * </p>
+ *
+ * @return {@code true} if this task was executed, {@code false} if it was already marked as completed.
+ */
+ public boolean execute();
+
+ /**
+ * Returns the current priority. Note that {@link Priority#COMPLETING} will be returned
+ * if this task is completing or has completed.
+ */
+ public Priority getPriority();
+
+ /**
+ * Attempts to set this task's priority level to the level specified.
+ *
+ * @param priority Specified priority level.
+ *
+ * @throws IllegalArgumentException If the priority is invalid
+ * @return {@code true} if successful, {@code false} if this task is completing or has completed or the queue
+ * this task was scheduled on was shutdown, or if the priority was already at the specified level.
+ */
+ public boolean setPriority(final Priority priority);
+
+ /**
+ * Attempts to raise the priority to the priority level specified.
+ *
+ * @param priority Priority specified
+ *
+ * @throws IllegalArgumentException If the priority is invalid
+ * @return {@code false} if the current task is completing, {@code true} if the priority was raised to the
+ * specified level or was already at the specified level or higher.
+ */
+ public boolean raisePriority(final Priority priority);
+
+ /**
+ * Attempts to lower the priority to the priority level specified.
+ *
+ * @param priority Priority specified
+ *
+ * @throws IllegalArgumentException If the priority is invalid
+ * @return {@code false} if the current task is completing, {@code true} if the priority was lowered to the
+ * specified level or was already at the specified level or lower.
+ */
+ public boolean lowerPriority(final Priority priority);
+
+ /**
+ * Returns the suborder id associated with this task.
+ * @return The suborder id associated with this task.
+ */
+ public long getSubOrder();
+
+ /**
+ * Sets the suborder id associated with this task. Ths function has no effect when this task
+ * is completing or is completed.
+ *
+ * @param subOrder Specified new sub order.
+ *
+ * @return {@code true} if successful, {@code false} if this task is completing or has completed or the queue
+ * this task was scheduled on was shutdown, or if the current suborder is the same as the new sub order.
+ */
+ public boolean setSubOrder(final long subOrder);
+
+ /**
+ * Attempts to raise the suborder to the suborder specified.
+ *
+ * @param subOrder Specified new sub order.
+ *
+ * @return {@code false} if the current task is completing, {@code true} if the suborder was raised to the
+ * specified suborder or was already at the specified suborder or higher.
+ */
+ public boolean raiseSubOrder(final long subOrder);
+
+ /**
+ * Attempts to lower the suborder to the suborder specified.
+ *
+ * @param subOrder Specified new sub order.
+ *
+ * @return {@code false} if the current task is completing, {@code true} if the suborder was lowered to the
+ * specified suborder or was already at the specified suborder or lower.
+ */
+ public boolean lowerSubOrder(final long subOrder);
+
+ /**
+ * Sets the priority and suborder id associated with this task. Ths function has no effect when this task
+ * is completing or is completed.
+ *
+ * @param priority Priority specified
+ * @param subOrder Specified new sub order.
+ * @return {@code true} if successful, {@code false} if this task is completing or has completed or the queue
+ * this task was scheduled on was shutdown, or if the current priority and suborder are the same as
+ * the parameters.
+ */
+ public boolean setPriorityAndSubOrder(final Priority priority, final long subOrder);
+ }
+}
diff --git a/src/main/java/ca/spottedleaf/concurrentutil/executor/queue/PrioritisedTaskQueue.java b/src/main/java/ca/spottedleaf/concurrentutil/executor/queue/PrioritisedTaskQueue.java
new file mode 100644
index 0000000000000000000000000000000000000000..edb8c6611bdc9aced2714b963e00bbb7829603d2
--- /dev/null
+++ b/src/main/java/ca/spottedleaf/concurrentutil/executor/queue/PrioritisedTaskQueue.java
@@ -0,0 +1,454 @@
+package ca.spottedleaf.concurrentutil.executor.queue;
+
+import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor;
+import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
+import ca.spottedleaf.concurrentutil.util.Priority;
+import java.lang.invoke.VarHandle;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentSkipListMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+public final class PrioritisedTaskQueue implements PrioritisedExecutor {
+
+ /**
+ * Required for tie-breaking in the queue
+ */
+ private final AtomicLong taskIdGenerator = new AtomicLong();
+ private final AtomicLong scheduledTasks = new AtomicLong();
+ private final AtomicLong executedTasks = new AtomicLong();
+ private final AtomicLong subOrderGenerator = new AtomicLong();
+ private final AtomicBoolean shutdown = new AtomicBoolean();
+ private final ConcurrentSkipListMap<PrioritisedQueuedTask.Holder, Boolean> tasks = new ConcurrentSkipListMap<>(PrioritisedQueuedTask.COMPARATOR);
+
+ @Override
+ public long getTotalTasksScheduled() {
+ return this.scheduledTasks.get();
+ }
+
+ @Override
+ public long getTotalTasksExecuted() {
+ return this.executedTasks.get();
+ }
+
+ @Override
+ public long generateNextSubOrder() {
+ return this.subOrderGenerator.getAndIncrement();
+ }
+
+ @Override
+ public boolean shutdown() {
+ return !this.shutdown.getAndSet(true);
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return this.shutdown.get();
+ }
+
+ public PrioritisedTask peekFirst() {
+ final Map.Entry<PrioritisedQueuedTask.Holder, Boolean> firstEntry = this.tasks.firstEntry();
+ return firstEntry == null ? null : firstEntry.getKey().task;
+ }
+
+ public Priority getHighestPriority() {
+ final Map.Entry<PrioritisedQueuedTask.Holder, Boolean> firstEntry = this.tasks.firstEntry();
+ return firstEntry == null ? null : Priority.getPriority(firstEntry.getKey().priority);
+ }
+
+ public boolean hasNoScheduledTasks() {
+ final long executedTasks = this.executedTasks.get();
+ final long scheduledTasks = this.scheduledTasks.get();
+
+ return executedTasks == scheduledTasks;
+ }
+
+ public PrioritySubOrderPair getHighestPrioritySubOrder() {
+ final Map.Entry<PrioritisedQueuedTask.Holder, Boolean> firstEntry = this.tasks.firstEntry();
+ if (firstEntry == null) {
+ return null;
+ }
+
+ final PrioritisedQueuedTask.Holder holder = firstEntry.getKey();
+
+ return new PrioritySubOrderPair(Priority.getPriority(holder.priority), holder.subOrder);
+ }
+
+ public Runnable pollTask() {
+ for (;;) {
+ final Map.Entry<PrioritisedQueuedTask.Holder, Boolean> firstEntry = this.tasks.pollFirstEntry();
+ if (firstEntry != null) {
+ final PrioritisedQueuedTask.Holder task = firstEntry.getKey();
+ task.markRemoved();
+ if (!task.task.cancel()) {
+ continue;
+ }
+ return task.task.execute;
+ }
+
+ return null;
+ }
+ }
+
+ @Override
+ public boolean executeTask() {
+ for (;;) {
+ final Map.Entry<PrioritisedQueuedTask.Holder, Boolean> firstEntry = this.tasks.pollFirstEntry();
+ if (firstEntry != null) {
+ final PrioritisedQueuedTask.Holder task = firstEntry.getKey();
+ task.markRemoved();
+ if (!task.task.execute()) {
+ continue;
+ }
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ @Override
+ public PrioritisedTask createTask(final Runnable task) {
+ return this.createTask(task, Priority.NORMAL, this.generateNextSubOrder());
+ }
+
+ @Override
+ public PrioritisedTask createTask(final Runnable task, final Priority priority) {
+ return this.createTask(task, priority, this.generateNextSubOrder());
+ }
+
+ @Override
+ public PrioritisedTask createTask(final Runnable task, final Priority priority, final long subOrder) {
+ return new PrioritisedQueuedTask(task, priority, subOrder);
+ }
+
+ @Override
+ public PrioritisedTask queueTask(final Runnable task) {
+ return this.queueTask(task, Priority.NORMAL, this.generateNextSubOrder());
+ }
+
+ @Override
+ public PrioritisedTask queueTask(final Runnable task, final Priority priority) {
+ return this.queueTask(task, priority, this.generateNextSubOrder());
+ }
+
+ @Override
+ public PrioritisedTask queueTask(final Runnable task, final Priority priority, final long subOrder) {
+ final PrioritisedQueuedTask ret = new PrioritisedQueuedTask(task, priority, subOrder);
+
+ ret.queue();
+
+ return ret;
+ }
+
+ private final class PrioritisedQueuedTask implements PrioritisedExecutor.PrioritisedTask {
+ public static final Comparator<PrioritisedQueuedTask.Holder> COMPARATOR = (final PrioritisedQueuedTask.Holder t1, final PrioritisedQueuedTask.Holder t2) -> {
+ final int priorityCompare = t1.priority - t2.priority;
+ if (priorityCompare != 0) {
+ return priorityCompare;
+ }
+
+ final int subOrderCompare = Long.compare(t1.subOrder, t2.subOrder);
+ if (subOrderCompare != 0) {
+ return subOrderCompare;
+ }
+
+ return Long.compare(t1.id, t2.id);
+ };
+
+ private static final class Holder {
+ private final PrioritisedQueuedTask task;
+ private final int priority;
+ private final long subOrder;
+ private final long id;
+
+ private volatile boolean removed;
+ private static final VarHandle REMOVED_HANDLE = ConcurrentUtil.getVarHandle(Holder.class, "removed", boolean.class);
+
+ private Holder(final PrioritisedQueuedTask task, final int priority, final long subOrder,
+ final long id) {
+ this.task = task;
+ this.priority = priority;
+ this.subOrder = subOrder;
+ this.id = id;
+ }
+
+ /**
+ * Returns true if marked as removed
+ */
+ public boolean markRemoved() {
+ return !(boolean)REMOVED_HANDLE.getAndSet((Holder)this, (boolean)true);
+ }
+ }
+
+ private final long id;
+ private final Runnable execute;
+
+ private Priority priority;
+ private long subOrder;
+ private Holder holder;
+
+ public PrioritisedQueuedTask(final Runnable execute, final Priority priority, final long subOrder) {
+ if (!Priority.isValidPriority(priority)) {
+ throw new IllegalArgumentException("Invalid priority " + priority);
+ }
+
+ this.execute = execute;
+ this.priority = priority;
+ this.subOrder = subOrder;
+ this.id = PrioritisedTaskQueue.this.taskIdGenerator.getAndIncrement();
+ }
+
+ @Override
+ public PrioritisedExecutor getExecutor() {
+ return PrioritisedTaskQueue.this;
+ }
+
+ @Override
+ public boolean queue() {
+ synchronized (this) {
+ if (this.holder != null || this.priority == Priority.COMPLETING) {
+ return false;
+ }
+
+ if (PrioritisedTaskQueue.this.isShutdown()) {
+ throw new IllegalStateException("Queue is shutdown");
+ }
+
+ final Holder holder = new Holder(this, this.priority.priority, this.subOrder, this.id);
+ this.holder = holder;
+
+ PrioritisedTaskQueue.this.scheduledTasks.getAndIncrement();
+ PrioritisedTaskQueue.this.tasks.put(holder, Boolean.TRUE);
+ }
+
+ if (PrioritisedTaskQueue.this.isShutdown()) {
+ this.cancel();
+ throw new IllegalStateException("Queue is shutdown");
+ }
+
+
+ return true;
+ }
+
+ @Override
+ public boolean isQueued() {
+ synchronized (this) {
+ return this.holder != null && this.priority != Priority.COMPLETING;
+ }
+ }
+
+ @Override
+ public boolean cancel() {
+ synchronized (this) {
+ if (this.priority == Priority.COMPLETING) {
+ return false;
+ }
+
+ this.priority = Priority.COMPLETING;
+
+ if (this.holder != null) {
+ if (this.holder.markRemoved()) {
+ PrioritisedTaskQueue.this.tasks.remove(this.holder);
+ }
+ PrioritisedTaskQueue.this.executedTasks.getAndIncrement();
+ }
+
+ return true;
+ }
+ }
+
+ @Override
+ public boolean execute() {
+ final boolean increaseExecuted;
+
+ synchronized (this) {
+ if (this.priority == Priority.COMPLETING) {
+ return false;
+ }
+
+ this.priority = Priority.COMPLETING;
+
+ if (increaseExecuted = (this.holder != null)) {
+ if (this.holder.markRemoved()) {
+ PrioritisedTaskQueue.this.tasks.remove(this.holder);
+ }
+ }
+ }
+
+ try {
+ this.execute.run();
+ return true;
+ } finally {
+ if (increaseExecuted) {
+ PrioritisedTaskQueue.this.executedTasks.getAndIncrement();
+ }
+ }
+ }
+
+ @Override
+ public Priority getPriority() {
+ synchronized (this) {
+ return this.priority;
+ }
+ }
+
+ @Override
+ public boolean setPriority(final Priority priority) {
+ synchronized (this) {
+ if (this.priority == Priority.COMPLETING || this.priority == priority) {
+ return false;
+ }
+
+ this.priority = priority;
+
+ if (this.holder != null) {
+ if (this.holder.markRemoved()) {
+ PrioritisedTaskQueue.this.tasks.remove(this.holder);
+ }
+ this.holder = new Holder(this, priority.priority, this.subOrder, this.id);
+ PrioritisedTaskQueue.this.tasks.put(this.holder, Boolean.TRUE);
+ }
+
+ return true;
+ }
+ }
+
+ @Override
+ public boolean raisePriority(final Priority priority) {
+ synchronized (this) {
+ if (this.priority == Priority.COMPLETING || this.priority.isHigherOrEqualPriority(priority)) {
+ return false;
+ }
+
+ this.priority = priority;
+
+ if (this.holder != null) {
+ if (this.holder.markRemoved()) {
+ PrioritisedTaskQueue.this.tasks.remove(this.holder);
+ }
+ this.holder = new Holder(this, priority.priority, this.subOrder, this.id);
+ PrioritisedTaskQueue.this.tasks.put(this.holder, Boolean.TRUE);
+ }
+
+ return true;
+ }
+ }
+
+ @Override
+ public boolean lowerPriority(Priority priority) {
+ synchronized (this) {
+ if (this.priority == Priority.COMPLETING || this.priority.isLowerOrEqualPriority(priority)) {
+ return false;
+ }
+
+ this.priority = priority;
+
+ if (this.holder != null) {
+ if (this.holder.markRemoved()) {
+ PrioritisedTaskQueue.this.tasks.remove(this.holder);
+ }
+ this.holder = new Holder(this, priority.priority, this.subOrder, this.id);
+ PrioritisedTaskQueue.this.tasks.put(this.holder, Boolean.TRUE);
+ }
+
+ return true;
+ }
+ }
+
+ @Override
+ public long getSubOrder() {
+ synchronized (this) {
+ return this.subOrder;
+ }
+ }
+
+ @Override
+ public boolean setSubOrder(final long subOrder) {
+ synchronized (this) {
+ if (this.priority == Priority.COMPLETING || this.subOrder == subOrder) {
+ return false;
+ }
+
+ this.subOrder = subOrder;
+
+ if (this.holder != null) {
+ if (this.holder.markRemoved()) {
+ PrioritisedTaskQueue.this.tasks.remove(this.holder);
+ }
+ this.holder = new Holder(this, priority.priority, this.subOrder, this.id);
+ PrioritisedTaskQueue.this.tasks.put(this.holder, Boolean.TRUE);
+ }
+
+ return true;
+ }
+ }
+
+ @Override
+ public boolean raiseSubOrder(long subOrder) {
+ synchronized (this) {
+ if (this.priority == Priority.COMPLETING || this.subOrder >= subOrder) {
+ return false;
+ }
+
+ this.subOrder = subOrder;
+
+ if (this.holder != null) {
+ if (this.holder.markRemoved()) {
+ PrioritisedTaskQueue.this.tasks.remove(this.holder);
+ }
+ this.holder = new Holder(this, priority.priority, this.subOrder, this.id);
+ PrioritisedTaskQueue.this.tasks.put(this.holder, Boolean.TRUE);
+ }
+
+ return true;
+ }
+ }
+
+ @Override
+ public boolean lowerSubOrder(final long subOrder) {
+ synchronized (this) {
+ if (this.priority == Priority.COMPLETING || this.subOrder <= subOrder) {
+ return false;
+ }
+
+ this.subOrder = subOrder;
+
+ if (this.holder != null) {
+ if (this.holder.markRemoved()) {
+ PrioritisedTaskQueue.this.tasks.remove(this.holder);
+ }
+ this.holder = new Holder(this, priority.priority, this.subOrder, this.id);
+ PrioritisedTaskQueue.this.tasks.put(this.holder, Boolean.TRUE);
+ }
+
+ return true;
+ }
+ }
+
+ @Override
+ public boolean setPriorityAndSubOrder(final Priority priority, final long subOrder) {
+ synchronized (this) {
+ if (this.priority == Priority.COMPLETING || (this.priority == priority && this.subOrder == subOrder)) {
+ return false;
+ }
+
+ this.priority = priority;
+ this.subOrder = subOrder;
+
+ if (this.holder != null) {
+ if (this.holder.markRemoved()) {
+ PrioritisedTaskQueue.this.tasks.remove(this.holder);
+ }
+ this.holder = new Holder(this, priority.priority, this.subOrder, this.id);
+ PrioritisedTaskQueue.this.tasks.put(this.holder, Boolean.TRUE);
+ }
+
+ return true;
+ }
+ }
+ }
+
+ public static record PrioritySubOrderPair(Priority priority, long subOrder) {}
+}
diff --git a/src/main/java/ca/spottedleaf/concurrentutil/executor/standard/DelayedPrioritisedTask.java b/src/main/java/ca/spottedleaf/concurrentutil/executor/standard/DelayedPrioritisedTask.java
deleted file mode 100644
index 3ce10053d4ec51855ad7012abb5d97df1c0e557a..0000000000000000000000000000000000000000
--- a/src/main/java/ca/spottedleaf/concurrentutil/executor/standard/DelayedPrioritisedTask.java
+++ /dev/null
@@ -1,170 +0,0 @@
-package ca.spottedleaf.concurrentutil.executor.standard;
-
-import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
-import java.lang.invoke.VarHandle;
-
-public class DelayedPrioritisedTask {
-
- protected volatile int priority;
- protected static final VarHandle PRIORITY_HANDLE = ConcurrentUtil.getVarHandle(DelayedPrioritisedTask.class, "priority", int.class);
-
- protected static final int PRIORITY_SET = Integer.MIN_VALUE >>> 0;
-
- protected final int getPriorityVolatile() {
- return (int)PRIORITY_HANDLE.getVolatile((DelayedPrioritisedTask)this);
- }
-
- protected final int compareAndExchangePriorityVolatile(final int expect, final int update) {
- return (int)PRIORITY_HANDLE.compareAndExchange((DelayedPrioritisedTask)this, (int)expect, (int)update);
- }
-
- protected final int getAndOrPriorityVolatile(final int val) {
- return (int)PRIORITY_HANDLE.getAndBitwiseOr((DelayedPrioritisedTask)this, (int)val);
- }
-
- protected final void setPriorityPlain(final int val) {
- PRIORITY_HANDLE.set((DelayedPrioritisedTask)this, (int)val);
- }
-
- protected volatile PrioritisedExecutor.PrioritisedTask task;
- protected static final VarHandle TASK_HANDLE = ConcurrentUtil.getVarHandle(DelayedPrioritisedTask.class, "task", PrioritisedExecutor.PrioritisedTask.class);
-
- protected PrioritisedExecutor.PrioritisedTask getTaskPlain() {
- return (PrioritisedExecutor.PrioritisedTask)TASK_HANDLE.get((DelayedPrioritisedTask)this);
- }
-
- protected PrioritisedExecutor.PrioritisedTask getTaskVolatile() {
- return (PrioritisedExecutor.PrioritisedTask)TASK_HANDLE.getVolatile((DelayedPrioritisedTask)this);
- }
-
- protected final PrioritisedExecutor.PrioritisedTask compareAndExchangeTaskVolatile(final PrioritisedExecutor.PrioritisedTask expect, final PrioritisedExecutor.PrioritisedTask update) {
- return (PrioritisedExecutor.PrioritisedTask)TASK_HANDLE.compareAndExchange((DelayedPrioritisedTask)this, (PrioritisedExecutor.PrioritisedTask)expect, (PrioritisedExecutor.PrioritisedTask)update);
- }
-
- public DelayedPrioritisedTask(final PrioritisedExecutor.Priority priority) {
- this.setPriorityPlain(priority.priority);
- }
-
- // only public for debugging
- public int getPriorityInternal() {
- return this.getPriorityVolatile();
- }
-
- public PrioritisedExecutor.PrioritisedTask getTask() {
- return this.getTaskVolatile();
- }
-
- public void setTask(final PrioritisedExecutor.PrioritisedTask task) {
- int priority = this.getPriorityVolatile();
-
- if (this.compareAndExchangeTaskVolatile(null, task) != null) {
- throw new IllegalStateException("setTask() called twice");
- }
-
- int failures = 0;
- for (;;) {
- task.setPriority(PrioritisedExecutor.Priority.getPriority(priority));
-
- if (priority == (priority = this.compareAndExchangePriorityVolatile(priority, priority | PRIORITY_SET))) {
- return;
- }
-
- ++failures;
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
- }
- }
-
- public PrioritisedExecutor.Priority getPriority() {
- final int priority = this.getPriorityVolatile();
- if ((priority & PRIORITY_SET) != 0) {
- return this.task.getPriority();
- }
-
- return PrioritisedExecutor.Priority.getPriority(priority);
- }
-
- public void raisePriority(final PrioritisedExecutor.Priority priority) {
- if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
- throw new IllegalArgumentException("Invalid priority " + priority);
- }
-
- int failures = 0;
- for (int curr = this.getPriorityVolatile();;) {
- if ((curr & PRIORITY_SET) != 0) {
- this.getTaskPlain().raisePriority(priority);
- return;
- }
-
- if (!priority.isLowerPriority(curr)) {
- return;
- }
-
- if (curr == (curr = this.compareAndExchangePriorityVolatile(curr, priority.priority))) {
- return;
- }
-
- // failed, retry
-
- ++failures;
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
- }
- }
-
- public void setPriority(final PrioritisedExecutor.Priority priority) {
- if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
- throw new IllegalArgumentException("Invalid priority " + priority);
- }
-
- int failures = 0;
- for (int curr = this.getPriorityVolatile();;) {
- if ((curr & PRIORITY_SET) != 0) {
- this.getTaskPlain().setPriority(priority);
- return;
- }
-
- if (curr == (curr = this.compareAndExchangePriorityVolatile(curr, priority.priority))) {
- return;
- }
-
- // failed, retry
-
- ++failures;
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
- }
- }
-
- public void lowerPriority(final PrioritisedExecutor.Priority priority) {
- if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
- throw new IllegalArgumentException("Invalid priority " + priority);
- }
-
- int failures = 0;
- for (int curr = this.getPriorityVolatile();;) {
- if ((curr & PRIORITY_SET) != 0) {
- this.getTaskPlain().lowerPriority(priority);
- return;
- }
-
- if (!priority.isHigherPriority(curr)) {
- return;
- }
-
- if (curr == (curr = this.compareAndExchangePriorityVolatile(curr, priority.priority))) {
- return;
- }
-
- // failed, retry
-
- ++failures;
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
- }
- }
-}
diff --git a/src/main/java/ca/spottedleaf/concurrentutil/executor/standard/PrioritisedExecutor.java b/src/main/java/ca/spottedleaf/concurrentutil/executor/standard/PrioritisedExecutor.java
deleted file mode 100644
index 91beb6f23f257cf265fe3150f760892e605f217a..0000000000000000000000000000000000000000
--- a/src/main/java/ca/spottedleaf/concurrentutil/executor/standard/PrioritisedExecutor.java
+++ /dev/null
@@ -1,276 +0,0 @@
-package ca.spottedleaf.concurrentutil.executor.standard;
-
-import ca.spottedleaf.concurrentutil.executor.BaseExecutor;
-
-/**
- * Implementation of {@link BaseExecutor} which schedules tasks to be executed by a given priority.
- * @see BaseExecutor
- */
-public interface PrioritisedExecutor extends BaseExecutor {
-
- public static enum Priority {
-
- /**
- * Priority value indicating the task has completed or is being completed.
- * This priority cannot be used to schedule tasks.
- */
- COMPLETING(-1),
-
- /**
- * Absolute highest priority, should only be used for when a task is blocking a time-critical thread.
- */
- BLOCKING(),
-
- /**
- * Should only be used for urgent but not time-critical tasks.
- */
- HIGHEST(),
-
- /**
- * Two priorities above normal.
- */
- HIGHER(),
-
- /**
- * One priority above normal.
- */
- HIGH(),
-
- /**
- * Default priority.
- */
- NORMAL(),
-
- /**
- * One priority below normal.
- */
- LOW(),
-
- /**
- * Two priorities below normal.
- */
- LOWER(),
-
- /**
- * Use for tasks that should eventually execute, but are not needed to.
- */
- LOWEST(),
-
- /**
- * Use for tasks that can be delayed indefinitely.
- */
- IDLE();
-
- // returns whether the priority can be scheduled
- public static boolean isValidPriority(final Priority priority) {
- return priority != null && priority != Priority.COMPLETING;
- }
-
- // returns the higher priority of the two
- public static Priority max(final Priority p1, final Priority p2) {
- return p1.isHigherOrEqualPriority(p2) ? p1 : p2;
- }
-
- // returns the lower priroity of the two
- public static Priority min(final Priority p1, final Priority p2) {
- return p1.isLowerOrEqualPriority(p2) ? p1 : p2;
- }
-
- public boolean isHigherOrEqualPriority(final Priority than) {
- return this.priority <= than.priority;
- }
-
- public boolean isHigherPriority(final Priority than) {
- return this.priority < than.priority;
- }
-
- public boolean isLowerOrEqualPriority(final Priority than) {
- return this.priority >= than.priority;
- }
-
- public boolean isLowerPriority(final Priority than) {
- return this.priority > than.priority;
- }
-
- public boolean isHigherOrEqualPriority(final int than) {
- return this.priority <= than;
- }
-
- public boolean isHigherPriority(final int than) {
- return this.priority < than;
- }
-
- public boolean isLowerOrEqualPriority(final int than) {
- return this.priority >= than;
- }
-
- public boolean isLowerPriority(final int than) {
- return this.priority > than;
- }
-
- public static boolean isHigherOrEqualPriority(final int priority, final int than) {
- return priority <= than;
- }
-
- public static boolean isHigherPriority(final int priority, final int than) {
- return priority < than;
- }
-
- public static boolean isLowerOrEqualPriority(final int priority, final int than) {
- return priority >= than;
- }
-
- public static boolean isLowerPriority(final int priority, final int than) {
- return priority > than;
- }
-
- static final Priority[] PRIORITIES = Priority.values();
-
- /** includes special priorities */
- public static final int TOTAL_PRIORITIES = PRIORITIES.length;
-
- public static final int TOTAL_SCHEDULABLE_PRIORITIES = TOTAL_PRIORITIES - 1;
-
- public static Priority getPriority(final int priority) {
- return PRIORITIES[priority + 1];
- }
-
- private static int priorityCounter;
-
- private static int nextCounter() {
- return priorityCounter++;
- }
-
- public final int priority;
-
- Priority() {
- this(nextCounter());
- }
-
- Priority(final int priority) {
- this.priority = priority;
- }
- }
-
- /**
- * Executes the next available task.
- * <p>
- * If there is a task with priority {@link PrioritisedExecutor.Priority#BLOCKING} available, then that such task is executed.
- * </p>
- * <p>
- * If there is a task with priority {@link PrioritisedExecutor.Priority#IDLE} available then that task is only executed
- * when there are no other tasks available with a higher priority.
- * </p>
- * <p>
- * If there are no tasks that have priority {@link PrioritisedExecutor.Priority#BLOCKING} or {@link PrioritisedExecutor.Priority#IDLE}, then
- * this function will be biased to execute tasks that have higher priorities.
- * </p>
- *
- * @return {@code true} if a task was executed, {@code false} otherwise
- * @throws IllegalStateException If the current thread is not allowed to execute a task
- */
- @Override
- public boolean executeTask() throws IllegalStateException;
-
- /**
- * Queues or executes a task at {@link Priority#NORMAL} priority.
- * @param task The task to run.
- *
- * @throws IllegalStateException If this queue has shutdown.
- * @throws NullPointerException If the task is null
- * @return {@code null} if the current thread immediately executed the task, else returns the prioritised task
- * associated with the parameter
- */
- public default PrioritisedTask queueRunnable(final Runnable task) {
- return this.queueRunnable(task, Priority.NORMAL);
- }
-
- /**
- * Queues or executes a task.
- *
- * @param task The task to run.
- * @param priority The priority for the task.
- *
- * @throws IllegalStateException If this queue has shutdown.
- * @throws NullPointerException If the task is null
- * @throws IllegalArgumentException If the priority is invalid.
- * @return {@code null} if the current thread immediately executed the task, else returns the prioritised task
- * associated with the parameter
- */
- public PrioritisedTask queueRunnable(final Runnable task, final Priority priority);
-
- /**
- * Creates, but does not execute or queue the task. The task must later be queued via {@link BaseTask#queue()}.
- *
- * @param task The task to run.
- *
- * @throws IllegalStateException If this queue has shutdown.
- * @throws NullPointerException If the task is null
- * @throws IllegalArgumentException If the priority is invalid.
- * @throws UnsupportedOperationException If this executor does not support lazily queueing tasks
- * @return The prioritised task associated with the parameters
- */
- public default PrioritisedTask createTask(final Runnable task) {
- return this.createTask(task, Priority.NORMAL);
- }
-
- /**
- * Creates, but does not execute or queue the task. The task must later be queued via {@link BaseTask#queue()}.
- *
- * @param task The task to run.
- * @param priority The priority for the task.
- *
- * @throws IllegalStateException If this queue has shutdown.
- * @throws NullPointerException If the task is null
- * @throws IllegalArgumentException If the priority is invalid.
- * @throws UnsupportedOperationException If this executor does not support lazily queueing tasks
- * @return The prioritised task associated with the parameters
- */
- public PrioritisedTask createTask(final Runnable task, final Priority priority);
-
- /**
- * Extension of {@link ca.spottedleaf.concurrentutil.executor.BaseExecutor.BaseTask} which adds functions
- * to retrieve and modify the task's associated priority.
- *
- * @see ca.spottedleaf.concurrentutil.executor.BaseExecutor.BaseTask
- */
- public static interface PrioritisedTask extends BaseTask {
-
- /**
- * Returns the current priority. Note that {@link Priority#COMPLETING} will be returned
- * if this task is completing or has completed.
- */
- public Priority getPriority();
-
- /**
- * Attempts to set this task's priority level to the level specified.
- *
- * @param priority Specified priority level.
- *
- * @throws IllegalArgumentException If the priority is invalid
- * @return {@code true} if successful, {@code false} if this task is completing or has completed or the queue
- * this task was scheduled on was shutdown, or if the priority was already at the specified level.
- */
- public boolean setPriority(final Priority priority);
-
- /**
- * Attempts to raise the priority to the priority level specified.
- *
- * @param priority Priority specified
- *
- * @throws IllegalArgumentException If the priority is invalid
- * @return {@code false} if the current task is completing, {@code true} if the priority was raised to the specified level or was already at the specified level or higher.
- */
- public boolean raisePriority(final Priority priority);
-
- /**
- * Attempts to lower the priority to the priority level specified.
- *
- * @param priority Priority specified
- *
- * @throws IllegalArgumentException If the priority is invalid
- * @return {@code false} if the current task is completing, {@code true} if the priority was lowered to the specified level or was already at the specified level or lower.
- */
- public boolean lowerPriority(final Priority priority);
- }
-}
diff --git a/src/main/java/ca/spottedleaf/concurrentutil/executor/standard/PrioritisedThreadPool.java b/src/main/java/ca/spottedleaf/concurrentutil/executor/standard/PrioritisedThreadPool.java
deleted file mode 100644
index 2ba36e29d0d8693f2f5e6c6d195ca27f2a5099aa..0000000000000000000000000000000000000000
--- a/src/main/java/ca/spottedleaf/concurrentutil/executor/standard/PrioritisedThreadPool.java
+++ /dev/null
@@ -1,632 +0,0 @@
-package ca.spottedleaf.concurrentutil.executor.standard;
-
-import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.TreeSet;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.BiConsumer;
-
-public final class PrioritisedThreadPool {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(PrioritisedThreadPool.class);
-
- private final PrioritisedThread[] threads;
- private final TreeSet<PrioritisedPoolExecutorImpl> queues = new TreeSet<>(PrioritisedPoolExecutorImpl.comparator());
- private final String name;
- private final long queueMaxHoldTime;
-
- private final ReferenceOpenHashSet<PrioritisedPoolExecutorImpl> nonShutdownQueues = new ReferenceOpenHashSet<>();
- private final ReferenceOpenHashSet<PrioritisedPoolExecutorImpl> activeQueues = new ReferenceOpenHashSet<>();
-
- private boolean shutdown;
-
- private long schedulingIdGenerator;
-
- private static final long DEFAULT_QUEUE_HOLD_TIME = (long)(5.0e6);
-
- /**
- * @param name Specified debug name of this thread pool
- * @param threads The number of threads to use
- */
- public PrioritisedThreadPool(final String name, final int threads) {
- this(name, threads, null);
- }
-
- /**
- * @param name Specified debug name of this thread pool
- * @param threads The number of threads to use
- * @param threadModifier Invoked for each created thread with its incremental id before starting them
- */
- public PrioritisedThreadPool(final String name, final int threads, final BiConsumer<Thread, Integer> threadModifier) {
- this(name, threads, threadModifier, DEFAULT_QUEUE_HOLD_TIME); // 5ms
- }
-
- /**
- * @param name Specified debug name of this thread pool
- * @param threads The number of threads to use
- * @param threadModifier Invoked for each created thread with its incremental id before starting them
- * @param queueHoldTime The maximum amount of time to spend executing tasks in a specific queue before attempting
- * to switch to another queue, per thread
- */
- public PrioritisedThreadPool(final String name, final int threads, final BiConsumer<Thread, Integer> threadModifier,
- final long queueHoldTime) { // in ns
- if (threads <= 0) {
- throw new IllegalArgumentException("Thread count must be > 0, not " + threads);
- }
- if (name == null) {
- throw new IllegalArgumentException("Name cannot be null");
- }
- this.name = name;
- this.queueMaxHoldTime = queueHoldTime;
-
- this.threads = new PrioritisedThread[threads];
- for (int i = 0; i < threads; ++i) {
- this.threads[i] = new PrioritisedThread(this);
-
- // set default attributes
- this.threads[i].setName("Prioritised thread for pool '" + name + "' #" + i);
- this.threads[i].setUncaughtExceptionHandler((final Thread thread, final Throwable throwable) -> {
- LOGGER.error("Uncaught exception in thread " + thread.getName(), throwable);
- });
-
- // let thread modifier override defaults
- if (threadModifier != null) {
- threadModifier.accept(this.threads[i], Integer.valueOf(i));
- }
-
- // now the thread can start
- this.threads[i].start();
- }
- }
-
- /**
- * Returns an array representing the threads backing this thread pool.
- */
- public Thread[] getThreads() {
- return Arrays.copyOf(this.threads, this.threads.length, Thread[].class);
- }
-
- /**
- * Creates and returns a {@link PrioritisedPoolExecutor} to schedule tasks onto. The returned executor will execute
- * tasks on this thread pool only.
- * @param name The debug name of the executor.
- * @param minParallelism The minimum number of threads to be executing tasks from the returned executor
- * before threads may be allocated to other queues in this thread pool.
- * @param parallelism The maximum number of threads which may be executing tasks from the returned executor.
- * @throws IllegalStateException If this thread pool is shut down
- */
- public PrioritisedPoolExecutor createExecutor(final String name, final int minParallelism, final int parallelism) {
- synchronized (this.nonShutdownQueues) {
- if (this.shutdown) {
- throw new IllegalStateException("Queue is shutdown: " + this.toString());
- }
- final PrioritisedPoolExecutorImpl ret = new PrioritisedPoolExecutorImpl(
- this, name,
- Math.min(Math.max(1, parallelism), this.threads.length),
- Math.min(Math.max(0, minParallelism), this.threads.length)
- );
-
- this.nonShutdownQueues.add(ret);
-
- synchronized (this.activeQueues) {
- this.activeQueues.add(ret);
- }
-
- return ret;
- }
- }
-
- /**
- * Prevents creation of new queues, shutdowns all non-shutdown queues if specified
- */
- public void halt(final boolean shutdownQueues) {
- synchronized (this.nonShutdownQueues) {
- this.shutdown = true;
- }
- if (shutdownQueues) {
- final ArrayList<PrioritisedPoolExecutorImpl> queuesToShutdown;
- synchronized (this.nonShutdownQueues) {
- this.shutdown = true;
- queuesToShutdown = new ArrayList<>(this.nonShutdownQueues);
- }
-
- for (final PrioritisedPoolExecutorImpl queue : queuesToShutdown) {
- queue.shutdown();
- }
- }
-
-
- for (final PrioritisedThread thread : this.threads) {
- // can't kill queue, queue is null
- thread.halt(false);
- }
- }
-
- /**
- * Waits until all threads in this pool have shutdown, or until the specified time has passed.
- * @param msToWait Maximum time to wait.
- * @return {@code false} if the maximum time passed, {@code true} otherwise.
- */
- public boolean join(final long msToWait) {
- try {
- return this.join(msToWait, false);
- } catch (final InterruptedException ex) {
- throw new IllegalStateException(ex);
- }
- }
-
- /**
- * Waits until all threads in this pool have shutdown, or until the specified time has passed.
- * @param msToWait Maximum time to wait.
- * @return {@code false} if the maximum time passed, {@code true} otherwise.
- * @throws InterruptedException If this thread is interrupted.
- */
- public boolean joinInterruptable(final long msToWait) throws InterruptedException {
- return this.join(msToWait, true);
- }
-
- protected final boolean join(final long msToWait, final boolean interruptable) throws InterruptedException {
- final long nsToWait = msToWait * (1000 * 1000);
- final long start = System.nanoTime();
- final long deadline = start + nsToWait;
- boolean interrupted = false;
- try {
- for (final PrioritisedThread thread : this.threads) {
- for (;;) {
- if (!thread.isAlive()) {
- break;
- }
- final long current = System.nanoTime();
- if (current >= deadline) {
- return false;
- }
-
- try {
- thread.join(Math.max(1L, (deadline - current) / (1000 * 1000)));
- } catch (final InterruptedException ex) {
- if (interruptable) {
- throw ex;
- }
- interrupted = true;
- }
- }
- }
-
- return true;
- } finally {
- if (interrupted) {
- Thread.currentThread().interrupt();
- }
- }
- }
-
- /**
- * Shuts down this thread pool, optionally waiting for all tasks to be executed.
- * This function will invoke {@link PrioritisedPoolExecutor#shutdown()} on all created executors on this
- * thread pool.
- * @param wait Whether to wait for tasks to be executed
- */
- public void shutdown(final boolean wait) {
- final ArrayList<PrioritisedPoolExecutorImpl> queuesToShutdown;
- synchronized (this.nonShutdownQueues) {
- this.shutdown = true;
- queuesToShutdown = new ArrayList<>(this.nonShutdownQueues);
- }
-
- for (final PrioritisedPoolExecutorImpl queue : queuesToShutdown) {
- queue.shutdown();
- }
-
- for (final PrioritisedThread thread : this.threads) {
- // none of these can be true or else NPE
- thread.close(false, false);
- }
-
- if (wait) {
- final ArrayList<PrioritisedPoolExecutorImpl> queues;
- synchronized (this.activeQueues) {
- queues = new ArrayList<>(this.activeQueues);
- }
- for (final PrioritisedPoolExecutorImpl queue : queues) {
- queue.waitUntilAllExecuted();
- }
- }
- }
-
- protected static final class PrioritisedThread extends PrioritisedQueueExecutorThread {
-
- protected final PrioritisedThreadPool pool;
- protected final AtomicBoolean alertedHighPriority = new AtomicBoolean();
-
- public PrioritisedThread(final PrioritisedThreadPool pool) {
- super(null);
- this.pool = pool;
- }
-
- public boolean alertHighPriorityExecutor() {
- if (!this.notifyTasks()) {
- if (!this.alertedHighPriority.get()) {
- this.alertedHighPriority.set(true);
- }
- return false;
- }
-
- return true;
- }
-
- private boolean isAlertedHighPriority() {
- return this.alertedHighPriority.get() && this.alertedHighPriority.getAndSet(false);
- }
-
- @Override
- protected boolean pollTasks() {
- final PrioritisedThreadPool pool = this.pool;
- final TreeSet<PrioritisedPoolExecutorImpl> queues = this.pool.queues;
-
- boolean ret = false;
- for (;;) {
- if (this.halted) {
- break;
- }
- // try to find a queue
- // note that if and ONLY IF the queues set is empty, this means there are no tasks for us to execute.
- // so we can only break when it's empty
- final PrioritisedPoolExecutorImpl queue;
- // select queue
- synchronized (queues) {
- queue = queues.pollFirst();
- if (queue == null) {
- // no tasks to execute
- break;
- }
-
- queue.schedulingId = ++pool.schedulingIdGenerator;
- // we own this queue now, so increment the executor count
- // do we also need to push this queue up for grabs for another executor?
- if (++queue.concurrentExecutors < queue.maximumExecutors) {
- // re-add to queues
- // it's very important this is done in the same synchronised block for polling, as this prevents
- // us from possibly later adding a queue that should not exist in the set
- queues.add(queue);
- queue.isQueued = true;
- } else {
- queue.isQueued = false;
- }
- // note: we cannot drain entries from the queue while holding this lock, as it will cause deadlock
- // the queue addition holds the per-queue lock first then acquires the lock we have now, but if we
- // try to poll now we don't hold the per queue lock but we do hold the global lock...
- }
-
- // parse tasks as long as we are allowed
- final long start = System.nanoTime();
- final long deadline = start + pool.queueMaxHoldTime;
- do {
- try {
- if (this.halted) {
- break;
- }
- if (!queue.executeTask()) {
- // no more tasks, try next queue
- break;
- }
- ret = true;
- } catch (final ThreadDeath death) {
- throw death; // goodbye world...
- } catch (final Throwable throwable) {
- LOGGER.error("Exception thrown from thread '" + this.getName() + "' in queue '" + queue.toString() + "'", throwable);
- }
- } while (!this.isAlertedHighPriority() && System.nanoTime() <= deadline);
-
- synchronized (queues) {
- // decrement executors, we are no longer executing
- if (queue.isQueued) {
- queues.remove(queue);
- queue.isQueued = false;
- }
- if (--queue.concurrentExecutors == 0 && queue.scheduledPriority == null) {
- // reset scheduling id once the queue is empty again
- // this will ensure empty queues are not prioritised suddenly over active queues once tasks are
- // queued
- queue.schedulingId = 0L;
- }
-
- // ensure the executor is queued for execution again
- if (!queue.isHalted && queue.scheduledPriority != null) { // make sure it actually has tasks
- queues.add(queue);
- queue.isQueued = true;
- }
- }
- }
-
- return ret;
- }
- }
-
- public interface PrioritisedPoolExecutor extends PrioritisedExecutor {
-
- /**
- * Removes this queue from the thread pool without shutting the queue down or waiting for queued tasks to be executed
- */
- public void halt();
-
- /**
- * Returns whether this executor is scheduled to run tasks or is running tasks, otherwise it returns whether
- * this queue is not halted and not shutdown.
- */
- public boolean isActive();
- }
-
- protected static final class PrioritisedPoolExecutorImpl extends PrioritisedThreadedTaskQueue implements PrioritisedPoolExecutor {
-
- protected final PrioritisedThreadPool pool;
- protected final long[] priorityCounts = new long[Priority.TOTAL_SCHEDULABLE_PRIORITIES];
- protected long schedulingId;
- protected int concurrentExecutors;
- protected Priority scheduledPriority;
-
- protected final String name;
- protected final int maximumExecutors;
- protected final int minimumExecutors;
- protected boolean isQueued;
-
- public PrioritisedPoolExecutorImpl(final PrioritisedThreadPool pool, final String name, final int maximumExecutors, final int minimumExecutors) {
- this.pool = pool;
- this.name = name;
- this.maximumExecutors = maximumExecutors;
- this.minimumExecutors = minimumExecutors;
- }
-
- public static Comparator<PrioritisedPoolExecutorImpl> comparator() {
- return (final PrioritisedPoolExecutorImpl p1, final PrioritisedPoolExecutorImpl p2) -> {
- if (p1 == p2) {
- return 0;
- }
-
- final int belowMin1 = p1.minimumExecutors - p1.concurrentExecutors;
- final int belowMin2 = p2.minimumExecutors - p2.concurrentExecutors;
-
- // test minimum executors
- if (belowMin1 > 0 || belowMin2 > 0) {
- // want the largest belowMin to be first
- final int minCompare = Integer.compare(belowMin2, belowMin1);
-
- if (minCompare != 0) {
- return minCompare;
- }
- }
-
- // prefer higher priority
- final int priorityCompare = p1.scheduledPriority.ordinal() - p2.scheduledPriority.ordinal();
- if (priorityCompare != 0) {
- return priorityCompare;
- }
-
- // try to spread out the executors so that each can have threads executing
- final int executorCompare = p1.concurrentExecutors - p2.concurrentExecutors;
- if (executorCompare != 0) {
- return executorCompare;
- }
-
- // if all else fails here we just choose whichever executor was queued first
- return Long.compare(p1.schedulingId, p2.schedulingId);
- };
- }
-
- private boolean isHalted;
-
- @Override
- public void halt() {
- final PrioritisedThreadPool pool = this.pool;
- final TreeSet<PrioritisedPoolExecutorImpl> queues = pool.queues;
- synchronized (queues) {
- if (this.isHalted) {
- return;
- }
- this.isHalted = true;
- if (this.isQueued) {
- queues.remove(this);
- this.isQueued = false;
- }
- }
- synchronized (pool.nonShutdownQueues) {
- pool.nonShutdownQueues.remove(this);
- }
- synchronized (pool.activeQueues) {
- pool.activeQueues.remove(this);
- }
- }
-
- @Override
- public boolean isActive() {
- final PrioritisedThreadPool pool = this.pool;
- final TreeSet<PrioritisedPoolExecutorImpl> queues = pool.queues;
-
- synchronized (queues) {
- if (this.concurrentExecutors != 0) {
- return true;
- }
- synchronized (pool.activeQueues) {
- if (pool.activeQueues.contains(this)) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- private long totalQueuedTasks = 0L;
-
- @Override
- protected void priorityChange(final PrioritisedThreadedTaskQueue.PrioritisedTask task, final Priority from, final Priority to) {
- // Note: The superclass' queue lock is ALWAYS held when inside this method. So we do NOT need to do any additional synchronisation
- // for accessing this queue's state.
- final long[] priorityCounts = this.priorityCounts;
- final boolean shutdown = this.isShutdown();
-
- if (from == null && to == Priority.COMPLETING) {
- throw new IllegalStateException("Cannot complete task without queueing it first");
- }
-
- // we should only notify for queueing of tasks, not changing priorities
- final boolean shouldNotifyTasks = from == null;
-
- final Priority scheduledPriority = this.scheduledPriority;
- if (from != null) {
- --priorityCounts[from.priority];
- }
- if (to != Priority.COMPLETING) {
- ++priorityCounts[to.priority];
- }
- final long totalQueuedTasks;
- if (to == Priority.COMPLETING) {
- totalQueuedTasks = --this.totalQueuedTasks;
- } else if (from == null) {
- totalQueuedTasks = ++this.totalQueuedTasks;
- } else {
- totalQueuedTasks = this.totalQueuedTasks;
- }
-
- // find new highest priority
- int highest = Math.min(to == Priority.COMPLETING ? Priority.IDLE.priority : to.priority, scheduledPriority == null ? Priority.IDLE.priority : scheduledPriority.priority);
- int lowestPriority = priorityCounts.length; // exclusive
- for (;highest < lowestPriority; ++highest) {
- final long count = priorityCounts[highest];
- if (count < 0) {
- throw new IllegalStateException("Priority " + highest + " has " + count + " scheduled tasks");
- }
-
- if (count != 0) {
- break;
- }
- }
-
- final Priority newPriority;
- if (highest == lowestPriority) {
- // no tasks left
- newPriority = null;
- } else if (shutdown) {
- // whichever is lower, the actual greatest priority or simply HIGHEST
- // this is so shutdown automatically gets priority
- newPriority = Priority.getPriority(Math.min(highest, Priority.HIGHEST.priority));
- } else {
- newPriority = Priority.getPriority(highest);
- }
-
- final int executorsWanted;
- boolean shouldNotifyHighPriority = false;
-
- final PrioritisedThreadPool pool = this.pool;
- final TreeSet<PrioritisedPoolExecutorImpl> queues = pool.queues;
-
- synchronized (queues) {
- if (!this.isQueued) {
- // see if we need to be queued
- if (newPriority != null) {
- if (this.schedulingId == 0L) {
- this.schedulingId = ++pool.schedulingIdGenerator;
- }
- this.scheduledPriority = newPriority; // must be updated before queue add
- if (!this.isHalted && this.concurrentExecutors < this.maximumExecutors) {
- shouldNotifyHighPriority = newPriority.isHigherOrEqualPriority(Priority.HIGH);
- queues.add(this);
- this.isQueued = true;
- }
- } else {
- // do not queue
- this.scheduledPriority = null;
- }
- } else {
- // see if we need to NOT be queued
- if (newPriority == null) {
- queues.remove(this);
- this.scheduledPriority = null;
- this.isQueued = false;
- } else if (scheduledPriority != newPriority) {
- // if our priority changed, we need to update it - which means removing and re-adding into the queue
- queues.remove(this);
- // only now can we update scheduledPriority, since we are no longer in queue
- this.scheduledPriority = newPriority;
- queues.add(this);
- shouldNotifyHighPriority = (scheduledPriority == null || scheduledPriority.isLowerPriority(Priority.HIGH)) && newPriority.isHigherOrEqualPriority(Priority.HIGH);
- }
- }
-
- if (this.isQueued) {
- executorsWanted = Math.min(this.maximumExecutors - this.concurrentExecutors, (int)totalQueuedTasks);
- } else {
- executorsWanted = 0;
- }
- }
-
- if (newPriority == null && shutdown) {
- synchronized (pool.activeQueues) {
- pool.activeQueues.remove(this);
- }
- }
-
- // Wake up the number of executors we want
- if (executorsWanted > 0 || (shouldNotifyTasks | shouldNotifyHighPriority)) {
- int notified = 0;
- for (final PrioritisedThread thread : pool.threads) {
- if ((shouldNotifyHighPriority ? thread.alertHighPriorityExecutor() : thread.notifyTasks())
- && (++notified >= executorsWanted)) {
- break;
- }
- }
- }
- }
-
- @Override
- public boolean shutdown() {
- final boolean ret = super.shutdown();
- if (!ret) {
- return ret;
- }
-
- final PrioritisedThreadPool pool = this.pool;
-
- // remove from active queues
- synchronized (pool.nonShutdownQueues) {
- pool.nonShutdownQueues.remove(this);
- }
-
- final TreeSet<PrioritisedPoolExecutorImpl> queues = pool.queues;
-
- // try and shift around our priority
- synchronized (queues) {
- if (this.scheduledPriority == null) {
- // no tasks are queued, ensure we aren't in activeQueues
- synchronized (pool.activeQueues) {
- pool.activeQueues.remove(this);
- }
-
- return ret;
- }
-
- // try to set scheduled priority to HIGHEST so it drains faster
-
- if (this.scheduledPriority.isHigherOrEqualPriority(Priority.HIGHEST)) {
- // already at target priority (highest or above)
- return ret;
- }
-
- // shift priority to HIGHEST
-
- if (this.isQueued) {
- queues.remove(this);
- this.scheduledPriority = Priority.HIGHEST;
- queues.add(this);
- } else {
- this.scheduledPriority = Priority.HIGHEST;
- }
- }
-
- return ret;
- }
- }
-}
diff --git a/src/main/java/ca/spottedleaf/concurrentutil/executor/standard/PrioritisedThreadedTaskQueue.java b/src/main/java/ca/spottedleaf/concurrentutil/executor/standard/PrioritisedThreadedTaskQueue.java
deleted file mode 100644
index 3e8401b1b1f833c4f01bc87059a2f48d761d989f..0000000000000000000000000000000000000000
--- a/src/main/java/ca/spottedleaf/concurrentutil/executor/standard/PrioritisedThreadedTaskQueue.java
+++ /dev/null
@@ -1,378 +0,0 @@
-package ca.spottedleaf.concurrentutil.executor.standard;
-
-import java.util.ArrayDeque;
-import java.util.concurrent.atomic.AtomicLong;
-
-public class PrioritisedThreadedTaskQueue implements PrioritisedExecutor {
-
- protected final ArrayDeque<PrioritisedTask>[] queues = new ArrayDeque[Priority.TOTAL_SCHEDULABLE_PRIORITIES]; {
- for (int i = 0; i < Priority.TOTAL_SCHEDULABLE_PRIORITIES; ++i) {
- this.queues[i] = new ArrayDeque<>();
- }
- }
-
- // Use AtomicLong to separate from the queue field, we don't want false sharing here.
- protected final AtomicLong totalScheduledTasks = new AtomicLong();
- protected final AtomicLong totalCompletedTasks = new AtomicLong();
-
- // this is here to prevent failures to queue stalling flush() calls (as the schedule calls would increment totalScheduledTasks without this check)
- protected volatile boolean hasShutdown;
-
- protected long taskIdGenerator = 0;
-
- @Override
- public PrioritisedExecutor.PrioritisedTask queueRunnable(final Runnable task, final Priority priority) throws IllegalStateException, IllegalArgumentException {
- if (!Priority.isValidPriority(priority)) {
- throw new IllegalArgumentException("Priority " + priority + " is invalid");
- }
- if (task == null) {
- throw new NullPointerException("Task cannot be null");
- }
-
- if (this.hasShutdown) {
- // prevent us from stalling flush() calls by incrementing scheduled tasks when we really didn't schedule something
- throw new IllegalStateException("Queue has shutdown");
- }
-
- final PrioritisedTask ret;
-
- synchronized (this.queues) {
- if (this.hasShutdown) {
- throw new IllegalStateException("Queue has shutdown");
- }
- this.getAndAddTotalScheduledTasksVolatile(1L);
-
- ret = new PrioritisedTask(this.taskIdGenerator++, task, priority, this);
-
- this.queues[ret.priority.priority].add(ret);
-
- // call priority change callback (note: only after we successfully queue!)
- this.priorityChange(ret, null, priority);
- }
-
- return ret;
- }
-
- @Override
- public PrioritisedExecutor.PrioritisedTask createTask(final Runnable task, final Priority priority) {
- if (!Priority.isValidPriority(priority)) {
- throw new IllegalArgumentException("Priority " + priority + " is invalid");
- }
- if (task == null) {
- throw new NullPointerException("Task cannot be null");
- }
-
- return new PrioritisedTask(task, priority, this);
- }
-
- @Override
- public long getTotalTasksScheduled() {
- return this.totalScheduledTasks.get();
- }
-
- @Override
- public long getTotalTasksExecuted() {
- return this.totalCompletedTasks.get();
- }
-
- // callback method for subclasses to override
- // from is null when a task is immediately created
- protected void priorityChange(final PrioritisedTask task, final Priority from, final Priority to) {}
-
- /**
- * Polls the highest priority task currently available. {@code null} if none. This will mark the
- * returned task as completed.
- */
- protected PrioritisedTask poll() {
- return this.poll(Priority.IDLE);
- }
-
- protected PrioritisedTask poll(final Priority minPriority) {
- final ArrayDeque<PrioritisedTask>[] queues = this.queues;
- synchronized (queues) {
- final int max = minPriority.priority;
- for (int i = 0; i <= max; ++i) {
- final ArrayDeque<PrioritisedTask> queue = queues[i];
- PrioritisedTask task;
- while ((task = queue.pollFirst()) != null) {
- if (task.trySetCompleting(i)) {
- return task;
- }
- }
- }
- }
-
- return null;
- }
-
- /**
- * Polls and executes the highest priority task currently available. Exceptions thrown during task execution will
- * be rethrown.
- * @return {@code true} if a task was executed, {@code false} otherwise.
- */
- @Override
- public boolean executeTask() {
- final PrioritisedTask task = this.poll();
-
- if (task != null) {
- task.executeInternal();
- return true;
- }
-
- return false;
- }
-
- @Override
- public boolean shutdown() {
- synchronized (this.queues) {
- if (this.hasShutdown) {
- return false;
- }
- this.hasShutdown = true;
- }
- return true;
- }
-
- @Override
- public boolean isShutdown() {
- return this.hasShutdown;
- }
-
- /* totalScheduledTasks */
-
- protected final long getTotalScheduledTasksVolatile() {
- return this.totalScheduledTasks.get();
- }
-
- protected final long getAndAddTotalScheduledTasksVolatile(final long value) {
- return this.totalScheduledTasks.getAndAdd(value);
- }
-
- /* totalCompletedTasks */
-
- protected final long getTotalCompletedTasksVolatile() {
- return this.totalCompletedTasks.get();
- }
-
- protected final long getAndAddTotalCompletedTasksVolatile(final long value) {
- return this.totalCompletedTasks.getAndAdd(value);
- }
-
- protected static final class PrioritisedTask implements PrioritisedExecutor.PrioritisedTask {
- protected final PrioritisedThreadedTaskQueue queue;
- protected long id;
- protected static final long NOT_SCHEDULED_ID = -1L;
-
- protected Runnable runnable;
- protected volatile Priority priority;
-
- protected PrioritisedTask(final long id, final Runnable runnable, final Priority priority, final PrioritisedThreadedTaskQueue queue) {
- if (!Priority.isValidPriority(priority)) {
- throw new IllegalArgumentException("Invalid priority " + priority);
- }
-
- this.priority = priority;
- this.runnable = runnable;
- this.queue = queue;
- this.id = id;
- }
-
- protected PrioritisedTask(final Runnable runnable, final Priority priority, final PrioritisedThreadedTaskQueue queue) {
- if (!Priority.isValidPriority(priority)) {
- throw new IllegalArgumentException("Invalid priority " + priority);
- }
-
- this.priority = priority;
- this.runnable = runnable;
- this.queue = queue;
- this.id = NOT_SCHEDULED_ID;
- }
-
- @Override
- public boolean queue() {
- if (this.queue.hasShutdown) {
- throw new IllegalStateException("Queue has shutdown");
- }
-
- synchronized (this.queue.queues) {
- if (this.queue.hasShutdown) {
- throw new IllegalStateException("Queue has shutdown");
- }
-
- final Priority priority = this.priority;
- if (priority == Priority.COMPLETING) {
- return false;
- }
-
- if (this.id != NOT_SCHEDULED_ID) {
- return false;
- }
-
- this.queue.getAndAddTotalScheduledTasksVolatile(1L);
- this.id = this.queue.taskIdGenerator++;
- this.queue.queues[priority.priority].add(this);
-
- this.queue.priorityChange(this, null, priority);
-
- return true;
- }
- }
-
- protected boolean trySetCompleting(final int minPriority) {
- final Priority oldPriority = this.priority;
- if (oldPriority != Priority.COMPLETING && oldPriority.isHigherOrEqualPriority(minPriority)) {
- this.priority = Priority.COMPLETING;
- if (this.id != NOT_SCHEDULED_ID) {
- this.queue.priorityChange(this, oldPriority, Priority.COMPLETING);
- }
- return true;
- }
-
- return false;
- }
-
- @Override
- public Priority getPriority() {
- return this.priority;
- }
-
- @Override
- public boolean setPriority(final Priority priority) {
- if (!Priority.isValidPriority(priority)) {
- throw new IllegalArgumentException("Invalid priority " + priority);
- }
- synchronized (this.queue.queues) {
- final Priority curr = this.priority;
-
- if (curr == Priority.COMPLETING) {
- return false;
- }
-
- if (curr == priority) {
- return true;
- }
-
- this.priority = priority;
- if (this.id != NOT_SCHEDULED_ID) {
- this.queue.queues[priority.priority].add(this);
-
- // call priority change callback
- this.queue.priorityChange(this, curr, priority);
- }
- }
-
- return true;
- }
-
- @Override
- public boolean raisePriority(final Priority priority) {
- if (!Priority.isValidPriority(priority)) {
- throw new IllegalArgumentException("Invalid priority " + priority);
- }
-
- synchronized (this.queue.queues) {
- final Priority curr = this.priority;
-
- if (curr == Priority.COMPLETING) {
- return false;
- }
-
- if (curr.isHigherOrEqualPriority(priority)) {
- return true;
- }
-
- this.priority = priority;
- if (this.id != NOT_SCHEDULED_ID) {
- this.queue.queues[priority.priority].add(this);
-
- // call priority change callback
- this.queue.priorityChange(this, curr, priority);
- }
- }
-
- return true;
- }
-
- @Override
- public boolean lowerPriority(final Priority priority) {
- if (!Priority.isValidPriority(priority)) {
- throw new IllegalArgumentException("Invalid priority " + priority);
- }
-
- synchronized (this.queue.queues) {
- final Priority curr = this.priority;
-
- if (curr == Priority.COMPLETING) {
- return false;
- }
-
- if (curr.isLowerOrEqualPriority(priority)) {
- return true;
- }
-
- this.priority = priority;
- if (this.id != NOT_SCHEDULED_ID) {
- this.queue.queues[priority.priority].add(this);
-
- // call priority change callback
- this.queue.priorityChange(this, curr, priority);
- }
- }
-
- return true;
- }
-
- @Override
- public boolean cancel() {
- final long id;
- synchronized (this.queue.queues) {
- final Priority oldPriority = this.priority;
- if (oldPriority == Priority.COMPLETING) {
- return false;
- }
-
- this.priority = Priority.COMPLETING;
- // call priority change callback
- if ((id = this.id) != NOT_SCHEDULED_ID) {
- this.queue.priorityChange(this, oldPriority, Priority.COMPLETING);
- }
- }
- this.runnable = null;
- if (id != NOT_SCHEDULED_ID) {
- this.queue.getAndAddTotalCompletedTasksVolatile(1L);
- }
- return true;
- }
-
- protected void executeInternal() {
- try {
- final Runnable execute = this.runnable;
- this.runnable = null;
- execute.run();
- } finally {
- if (this.id != NOT_SCHEDULED_ID) {
- this.queue.getAndAddTotalCompletedTasksVolatile(1L);
- }
- }
- }
-
- @Override
- public boolean execute() {
- synchronized (this.queue.queues) {
- final Priority oldPriority = this.priority;
- if (oldPriority == Priority.COMPLETING) {
- return false;
- }
-
- this.priority = Priority.COMPLETING;
- // call priority change callback
- if (this.id != NOT_SCHEDULED_ID) {
- this.queue.priorityChange(this, oldPriority, Priority.COMPLETING);
- }
- }
-
- this.executeInternal();
- return true;
- }
- }
-}
diff --git a/src/main/java/ca/spottedleaf/concurrentutil/executor/standard/PrioritisedQueueExecutorThread.java b/src/main/java/ca/spottedleaf/concurrentutil/executor/thread/PrioritisedQueueExecutorThread.java
similarity index 60%
rename from src/main/java/ca/spottedleaf/concurrentutil/executor/standard/PrioritisedQueueExecutorThread.java
rename to src/main/java/ca/spottedleaf/concurrentutil/executor/thread/PrioritisedQueueExecutorThread.java
index d1683ba6350e530373944f98192c0f2baf241e70..f5367a13aaa02f0f929813c00a67e6ac7c8652cb 100644
--- a/src/main/java/ca/spottedleaf/concurrentutil/executor/standard/PrioritisedQueueExecutorThread.java
+++ b/src/main/java/ca/spottedleaf/concurrentutil/executor/thread/PrioritisedQueueExecutorThread.java
@@ -1,6 +1,8 @@
-package ca.spottedleaf.concurrentutil.executor.standard;
+package ca.spottedleaf.concurrentutil.executor.thread;
+import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor;
import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
+import ca.spottedleaf.concurrentutil.util.Priority;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.invoke.VarHandle;
@@ -11,8 +13,7 @@ import java.util.concurrent.locks.LockSupport;
* <p>
* Note: When using this thread, queue additions to the underlying {@link #queue} are not sufficient to get this thread
* to execute the task. The function {@link #notifyTasks()} must be used after scheduling a task. For expected behaviour
- * of task scheduling (thread wakes up after tasks are scheduled), use the methods provided on {@link PrioritisedExecutor}
- * methods.
+ * of task scheduling, use the methods provided on this class to schedule tasks.
* </p>
*/
public class PrioritisedQueueExecutorThread extends Thread implements PrioritisedExecutor {
@@ -30,7 +31,7 @@ public class PrioritisedQueueExecutorThread extends Thread implements Prioritise
protected final long spinWaitTime;
- static final long DEFAULT_SPINWAIT_TIME = (long)(0.1e6);// 0.1ms
+ protected static final long DEFAULT_SPINWAIT_TIME = (long)(0.1e6);// 0.1ms
public PrioritisedQueueExecutorThread(final PrioritisedExecutor queue) {
this(queue, DEFAULT_SPINWAIT_TIME); // 0.1ms
@@ -42,7 +43,16 @@ public class PrioritisedQueueExecutorThread extends Thread implements Prioritise
}
@Override
- public void run() {
+ public final void run() {
+ try {
+ this.begin();
+ this.doRun();
+ } finally {
+ this.die();
+ }
+ }
+
+ public final void doRun() {
final long spinWaitTime = this.spinWaitTime;
main_loop:
@@ -80,7 +90,7 @@ public class PrioritisedQueueExecutorThread extends Thread implements Prioritise
this.setThreadParkedVolatile(true);
// We need to parse here to avoid a race condition where a thread queues a task before we set parked to true
- // (i.e it will not notify us)
+ // (i.e. it will not notify us)
if (this.pollTasks()) {
this.setThreadParkedVolatile(false);
continue;
@@ -99,6 +109,10 @@ public class PrioritisedQueueExecutorThread extends Thread implements Prioritise
}
}
+ protected void begin() {}
+
+ protected void die() {}
+
/**
* Attempts to poll as many tasks as possible, returning when finished.
* @return Whether any tasks were executed.
@@ -115,8 +129,6 @@ public class PrioritisedQueueExecutorThread extends Thread implements Prioritise
break;
}
ret = true;
- } catch (final ThreadDeath death) {
- throw death; // goodbye world...
} catch (final Throwable throwable) {
LOGGER.error("Exception thrown from prioritized runnable task in thread '" + this.getName() + "'", throwable);
}
@@ -146,95 +158,86 @@ public class PrioritisedQueueExecutorThread extends Thread implements Prioritise
}
@Override
- public PrioritisedTask createTask(final Runnable task, final Priority priority) {
- final PrioritisedTask queueTask = this.queue.createTask(task, priority);
-
- // need to override queue() to notify us of tasks
- return new PrioritisedTask() {
- @Override
- public Priority getPriority() {
- return queueTask.getPriority();
- }
-
- @Override
- public boolean setPriority(final Priority priority) {
- return queueTask.setPriority(priority);
- }
+ public long getTotalTasksExecuted() {
+ return this.queue.getTotalTasksExecuted();
+ }
- @Override
- public boolean raisePriority(final Priority priority) {
- return queueTask.raisePriority(priority);
- }
+ @Override
+ public long getTotalTasksScheduled() {
+ return this.queue.getTotalTasksScheduled();
+ }
- @Override
- public boolean lowerPriority(final Priority priority) {
- return queueTask.lowerPriority(priority);
- }
+ @Override
+ public long generateNextSubOrder() {
+ return this.queue.generateNextSubOrder();
+ }
- @Override
- public boolean queue() {
- final boolean ret = queueTask.queue();
- if (ret) {
- PrioritisedQueueExecutorThread.this.notifyTasks();
- }
- return ret;
- }
+ @Override
+ public boolean shutdown() {
+ throw new UnsupportedOperationException();
+ }
- @Override
- public boolean cancel() {
- return queueTask.cancel();
- }
+ @Override
+ public boolean isShutdown() {
+ return false;
+ }
- @Override
- public boolean execute() {
- return queueTask.execute();
- }
- };
+ /**
+ * {@inheritDoc}
+ * @throws IllegalStateException Always
+ */
+ @Override
+ public boolean executeTask() throws IllegalStateException {
+ throw new IllegalStateException();
}
@Override
- public PrioritisedTask queueRunnable(final Runnable task, final Priority priority) {
- final PrioritisedTask ret = this.queue.queueRunnable(task, priority);
+ public PrioritisedTask queueTask(final Runnable task) {
+ final PrioritisedTask ret = this.createTask(task);
- this.notifyTasks();
+ ret.queue();
return ret;
}
@Override
- public boolean haveAllTasksExecuted() {
- return this.queue.haveAllTasksExecuted();
+ public PrioritisedTask queueTask(final Runnable task, final Priority priority) {
+ final PrioritisedTask ret = this.createTask(task, priority);
+
+ ret.queue();
+
+ return ret;
}
@Override
- public long getTotalTasksExecuted() {
- return this.queue.getTotalTasksExecuted();
+ public PrioritisedTask queueTask(final Runnable task, final Priority priority, final long subOrder) {
+ final PrioritisedTask ret = this.createTask(task, priority, subOrder);
+
+ ret.queue();
+
+ return ret;
}
+
@Override
- public long getTotalTasksScheduled() {
- return this.queue.getTotalTasksScheduled();
+ public PrioritisedTask createTask(Runnable task) {
+ final PrioritisedTask queueTask = this.queue.createTask(task);
+
+ return new WrappedTask(queueTask);
}
- /**
- * {@inheritDoc}
- * @throws IllegalStateException If the current thread is {@code this} thread, or the underlying queue throws this exception.
- */
@Override
- public void waitUntilAllExecuted() throws IllegalStateException {
- if (Thread.currentThread() == this) {
- throw new IllegalStateException("Cannot block on our own queue");
- }
- this.queue.waitUntilAllExecuted();
+ public PrioritisedTask createTask(final Runnable task, final Priority priority) {
+ final PrioritisedTask queueTask = this.queue.createTask(task, priority);
+
+ return new WrappedTask(queueTask);
}
- /**
- * {@inheritDoc}
- * @throws IllegalStateException Always
- */
@Override
- public boolean executeTask() throws IllegalStateException {
- throw new IllegalStateException();
+ public PrioritisedTask createTask(final Runnable task, final Priority priority, final long subOrder) {
+ final PrioritisedTask queueTask = this.queue.createTask(task, priority, subOrder);
+
+ return new WrappedTask(queueTask);
}
/**
@@ -242,7 +245,7 @@ public class PrioritisedQueueExecutorThread extends Thread implements Prioritise
* <p>
* This function is MT-Safe.
* </p>
- * @param wait If this call is to wait until the queue is empty and there are no tasks executing in the queue.
+ * @param wait If this call is to wait until this thread shuts down.
* @param killQueue Whether to shutdown this thread's queue
* @return whether this thread shut down the queue
* @see #halt(boolean)
@@ -256,7 +259,20 @@ public class PrioritisedQueueExecutorThread extends Thread implements Prioritise
LockSupport.unpark(this);
if (wait) {
- this.waitUntilAllExecuted();
+ boolean interrupted = false;
+ for (;;) {
+ if (this.isAlive()) {
+ if (interrupted) {
+ Thread.currentThread().interrupt();
+ }
+ break;
+ }
+ try {
+ this.join();
+ } catch (final InterruptedException ex) {
+ interrupted = true;
+ }
+ }
}
return ret;
@@ -298,4 +314,89 @@ public class PrioritisedQueueExecutorThread extends Thread implements Prioritise
protected final void setThreadParkedVolatile(final boolean value) {
THREAD_PARKED_HANDLE.setVolatile(this, value);
}
+
+ /**
+ * Required so that queue() can notify (unpark) this thread
+ */
+ private final class WrappedTask implements PrioritisedTask {
+ private final PrioritisedTask queueTask;
+
+ public WrappedTask(final PrioritisedTask queueTask) {
+ this.queueTask = queueTask;
+ }
+
+ @Override
+ public PrioritisedExecutor getExecutor() {
+ return PrioritisedQueueExecutorThread.this;
+ }
+
+ @Override
+ public boolean queue() {
+ final boolean ret = this.queueTask.queue();
+ if (ret) {
+ PrioritisedQueueExecutorThread.this.notifyTasks();
+ }
+ return ret;
+ }
+
+ @Override
+ public boolean isQueued() {
+ return this.queueTask.isQueued();
+ }
+
+ @Override
+ public boolean cancel() {
+ return this.queueTask.cancel();
+ }
+
+ @Override
+ public boolean execute() {
+ return this.queueTask.execute();
+ }
+
+ @Override
+ public Priority getPriority() {
+ return this.queueTask.getPriority();
+ }
+
+ @Override
+ public boolean setPriority(final Priority priority) {
+ return this.queueTask.setPriority(priority);
+ }
+
+ @Override
+ public boolean raisePriority(final Priority priority) {
+ return this.queueTask.raisePriority(priority);
+ }
+
+ @Override
+ public boolean lowerPriority(final Priority priority) {
+ return this.queueTask.lowerPriority(priority);
+ }
+
+ @Override
+ public long getSubOrder() {
+ return this.queueTask.getSubOrder();
+ }
+
+ @Override
+ public boolean setSubOrder(final long subOrder) {
+ return this.queueTask.setSubOrder(subOrder);
+ }
+
+ @Override
+ public boolean raiseSubOrder(final long subOrder) {
+ return this.queueTask.raiseSubOrder(subOrder);
+ }
+
+ @Override
+ public boolean lowerSubOrder(final long subOrder) {
+ return this.queueTask.lowerSubOrder(subOrder);
+ }
+
+ @Override
+ public boolean setPriorityAndSubOrder(final Priority priority, final long subOrder) {
+ return this.queueTask.setPriorityAndSubOrder(priority, subOrder);
+ }
+ }
}
diff --git a/src/main/java/ca/spottedleaf/concurrentutil/executor/thread/PrioritisedThreadPool.java b/src/main/java/ca/spottedleaf/concurrentutil/executor/thread/PrioritisedThreadPool.java
new file mode 100644
index 0000000000000000000000000000000000000000..cb9df914a9a6d0d3f58fa58d8c93f4f583416cd1
--- /dev/null
+++ b/src/main/java/ca/spottedleaf/concurrentutil/executor/thread/PrioritisedThreadPool.java
@@ -0,0 +1,741 @@
+package ca.spottedleaf.concurrentutil.executor.thread;
+
+import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor;
+import ca.spottedleaf.concurrentutil.executor.queue.PrioritisedTaskQueue;
+import ca.spottedleaf.concurrentutil.util.Priority;
+import ca.spottedleaf.concurrentutil.util.TimeUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Consumer;
+
+public final class PrioritisedThreadPool {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(PrioritisedThreadPool.class);
+
+ private final Consumer<Thread> threadModifier;
+ private final COWArrayList<ExecutorGroup> executors = new COWArrayList<>(ExecutorGroup.class);
+ private final COWArrayList<PrioritisedThread> threads = new COWArrayList<>(PrioritisedThread.class);
+ private final COWArrayList<PrioritisedThread> aliveThreads = new COWArrayList<>(PrioritisedThread.class);
+
+ private static final Priority HIGH_PRIORITY_NOTIFY_THRESHOLD = Priority.HIGH;
+ private static final Priority QUEUE_SHUTDOWN_PRIORITY = Priority.HIGH;
+
+ private boolean shutdown;
+
+ public PrioritisedThreadPool(final Consumer<Thread> threadModifier) {
+ this.threadModifier = threadModifier;
+
+ if (threadModifier == null) {
+ throw new NullPointerException("Thread factory may not be null");
+ }
+ }
+
+ public Thread[] getAliveThreads() {
+ final PrioritisedThread[] threads = this.aliveThreads.getArray();
+
+ return Arrays.copyOf(threads, threads.length, Thread[].class);
+ }
+
+ public Thread[] getCoreThreads() {
+ final PrioritisedThread[] threads = this.threads.getArray();
+
+ return Arrays.copyOf(threads, threads.length, Thread[].class);
+ }
+
+ /**
+ * Prevents creation of new queues, shutdowns all non-shutdown queues if specified
+ */
+ public void halt(final boolean shutdownQueues) {
+ synchronized (this) {
+ this.shutdown = true;
+ }
+
+ if (shutdownQueues) {
+ for (final ExecutorGroup group : this.executors.getArray()) {
+ for (final ExecutorGroup.ThreadPoolExecutor executor : group.executors.getArray()) {
+ executor.shutdown();
+ }
+ }
+ }
+
+ for (final PrioritisedThread thread : this.threads.getArray()) {
+ thread.halt(false);
+ }
+ }
+
+ /**
+ * Waits until all threads in this pool have shutdown, or until the specified time has passed.
+ * @param msToWait Maximum time to wait.
+ * @return {@code false} if the maximum time passed, {@code true} otherwise.
+ */
+ public boolean join(final long msToWait) {
+ try {
+ return this.join(msToWait, false);
+ } catch (final InterruptedException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+
+ /**
+ * Waits until all threads in this pool have shutdown, or until the specified time has passed.
+ * @param msToWait Maximum time to wait.
+ * @return {@code false} if the maximum time passed, {@code true} otherwise.
+ * @throws InterruptedException If this thread is interrupted.
+ */
+ public boolean joinInterruptable(final long msToWait) throws InterruptedException {
+ return this.join(msToWait, true);
+ }
+
+ protected final boolean join(final long msToWait, final boolean interruptable) throws InterruptedException {
+ final long nsToWait = msToWait * (1000 * 1000);
+ final long start = System.nanoTime();
+ final long deadline = start + nsToWait;
+ boolean interrupted = false;
+ try {
+ for (final PrioritisedThread thread : this.aliveThreads.getArray()) {
+ for (;;) {
+ if (!thread.isAlive()) {
+ break;
+ }
+ final long current = System.nanoTime();
+ if (current >= deadline && msToWait > 0L) {
+ return false;
+ }
+
+ try {
+ thread.join(msToWait <= 0L ? 0L : Math.max(1L, (deadline - current) / (1000 * 1000)));
+ } catch (final InterruptedException ex) {
+ if (interruptable) {
+ throw ex;
+ }
+ interrupted = true;
+ }
+ }
+ }
+
+ return true;
+ } finally {
+ if (interrupted) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ /**
+ * Shuts down this thread pool, optionally waiting for all tasks to be executed.
+ * This function will invoke {@link PrioritisedExecutor#shutdown()} on all created executors on this
+ * thread pool.
+ * @param wait Whether to wait for tasks to be executed
+ */
+ public void shutdown(final boolean wait) {
+ synchronized (this) {
+ this.shutdown = true;
+ }
+
+ for (final ExecutorGroup group : this.executors.getArray()) {
+ for (final ExecutorGroup.ThreadPoolExecutor executor : group.executors.getArray()) {
+ executor.shutdown();
+ }
+ }
+
+
+ for (final PrioritisedThread thread : this.threads.getArray()) {
+ // none of these can be true or else NPE
+ thread.close(false, false);
+ }
+
+ if (wait) {
+ this.join(0L);
+ }
+ }
+
+ private void die(final PrioritisedThread thread) {
+ this.aliveThreads.remove(thread);
+ }
+
+ public void adjustThreadCount(final int threads) {
+ synchronized (this) {
+ if (this.shutdown) {
+ return;
+ }
+
+ final PrioritisedThread[] currentThreads = this.threads.getArray();
+ if (threads == currentThreads.length) {
+ // no adjustment needed
+ return;
+ }
+
+ if (threads < currentThreads.length) {
+ // we need to trim threads
+ for (int i = 0, difference = currentThreads.length - threads; i < difference; ++i) {
+ final PrioritisedThread remove = currentThreads[currentThreads.length - i - 1];
+
+ remove.halt(false);
+ this.threads.remove(remove);
+ }
+ } else {
+ // we need to add threads
+ for (int i = 0, difference = threads - currentThreads.length; i < difference; ++i) {
+ final PrioritisedThread thread = new PrioritisedThread();
+
+ this.threadModifier.accept(thread);
+ this.aliveThreads.add(thread);
+ this.threads.add(thread);
+
+ thread.start();
+ }
+ }
+ }
+ }
+
+ private static int compareInsideGroup(final ExecutorGroup.ThreadPoolExecutor src, final Priority srcPriority,
+ final ExecutorGroup.ThreadPoolExecutor dst, final Priority dstPriority) {
+ final int priorityCompare = srcPriority.ordinal() - dstPriority.ordinal();
+ if (priorityCompare != 0) {
+ return priorityCompare;
+ }
+
+ final int parallelismCompare = src.currentParallelism - dst.currentParallelism;
+ if (parallelismCompare != 0) {
+ return parallelismCompare;
+ }
+
+ return TimeUtil.compareTimes(src.lastRetrieved, dst.lastRetrieved);
+ }
+
+ private static int compareOutsideGroup(final ExecutorGroup.ThreadPoolExecutor src, final Priority srcPriority,
+ final ExecutorGroup.ThreadPoolExecutor dst, final Priority dstPriority) {
+ if (src.getGroup().division == dst.getGroup().division) {
+ // can only compare priorities inside the same division
+ final int priorityCompare = srcPriority.ordinal() - dstPriority.ordinal();
+ if (priorityCompare != 0) {
+ return priorityCompare;
+ }
+ }
+
+ final int parallelismCompare = src.getGroup().currentParallelism - dst.getGroup().currentParallelism;
+ if (parallelismCompare != 0) {
+ return parallelismCompare;
+ }
+
+ return TimeUtil.compareTimes(src.lastRetrieved, dst.lastRetrieved);
+ }
+
+ private ExecutorGroup.ThreadPoolExecutor obtainQueue() {
+ final long time = System.nanoTime();
+ synchronized (this) {
+ ExecutorGroup.ThreadPoolExecutor ret = null;
+ Priority retPriority = null;
+
+ for (final ExecutorGroup executorGroup : this.executors.getArray()) {
+ ExecutorGroup.ThreadPoolExecutor highest = null;
+ Priority highestPriority = null;
+ for (final ExecutorGroup.ThreadPoolExecutor executor : executorGroup.executors.getArray()) {
+ final int maxParallelism = executor.maxParallelism;
+ if (maxParallelism > 0 && executor.currentParallelism >= maxParallelism) {
+ continue;
+ }
+
+ final Priority priority = executor.getTargetPriority();
+
+ if (priority == null) {
+ continue;
+ }
+
+ if (highestPriority == null || compareInsideGroup(highest, highestPriority, executor, priority) > 0) {
+ highest = executor;
+ highestPriority = priority;
+ }
+ }
+
+ if (highest == null) {
+ continue;
+ }
+
+ if (ret == null || compareOutsideGroup(ret, retPriority, highest, highestPriority) > 0) {
+ ret = highest;
+ retPriority = highestPriority;
+ }
+ }
+
+ if (ret != null) {
+ ret.lastRetrieved = time;
+ ++ret.currentParallelism;
+ ++ret.getGroup().currentParallelism;
+ return ret;
+ }
+
+ return ret;
+ }
+ }
+
+ private void returnQueue(final ExecutorGroup.ThreadPoolExecutor executor) {
+ synchronized (this) {
+ --executor.currentParallelism;
+ --executor.getGroup().currentParallelism;
+ }
+
+ if (executor.isShutdown() && executor.queue.hasNoScheduledTasks()) {
+ executor.getGroup().executors.remove(executor);
+ }
+ }
+
+ private void notifyAllThreads() {
+ for (final PrioritisedThread thread : this.threads.getArray()) {
+ thread.notifyTasks();
+ }
+ }
+
+ public ExecutorGroup createExecutorGroup(final int division, final int flags) {
+ synchronized (this) {
+ if (this.shutdown) {
+ throw new IllegalStateException("Queue is shutdown: " + this.toString());
+ }
+
+ final ExecutorGroup ret = new ExecutorGroup(division, flags);
+
+ this.executors.add(ret);
+
+ return ret;
+ }
+ }
+
+ private final class PrioritisedThread extends PrioritisedQueueExecutorThread {
+
+ private final AtomicBoolean alertedHighPriority = new AtomicBoolean();
+
+ public PrioritisedThread() {
+ super(null);
+ }
+
+ public boolean alertHighPriorityExecutor() {
+ if (!this.notifyTasks()) {
+ if (!this.alertedHighPriority.get()) {
+ this.alertedHighPriority.set(true);
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ private boolean isAlertedHighPriority() {
+ return this.alertedHighPriority.get() && this.alertedHighPriority.getAndSet(false);
+ }
+
+ @Override
+ protected void die() {
+ PrioritisedThreadPool.this.die(this);
+ }
+
+ @Override
+ protected boolean pollTasks() {
+ boolean ret = false;
+
+ for (;;) {
+ if (this.halted) {
+ break;
+ }
+
+ final ExecutorGroup.ThreadPoolExecutor executor = PrioritisedThreadPool.this.obtainQueue();
+ if (executor == null) {
+ break;
+ }
+ final long deadline = System.nanoTime() + executor.queueMaxHoldTime;
+ do {
+ try {
+ if (this.halted || executor.halt) {
+ break;
+ }
+ if (!executor.executeTask()) {
+ // no more tasks, try next queue
+ break;
+ }
+ ret = true;
+ } catch (final Throwable throwable) {
+ LOGGER.error("Exception thrown from thread '" + this.getName() + "' in queue '" + executor.toString() + "'", throwable);
+ }
+ } while (!this.isAlertedHighPriority() && System.nanoTime() <= deadline);
+
+ PrioritisedThreadPool.this.returnQueue(executor);
+ }
+
+
+ return ret;
+ }
+ }
+
+ public final class ExecutorGroup {
+
+ private final AtomicLong subOrderGenerator = new AtomicLong();
+ private final COWArrayList<ThreadPoolExecutor> executors = new COWArrayList<>(ThreadPoolExecutor.class);
+
+ private final int division;
+ private int currentParallelism;
+
+ private ExecutorGroup(final int division, final int flags) {
+ this.division = division;
+ }
+
+ public ThreadPoolExecutor[] getAllExecutors() {
+ return this.executors.getArray().clone();
+ }
+
+ private PrioritisedThreadPool getThreadPool() {
+ return PrioritisedThreadPool.this;
+ }
+
+ public ThreadPoolExecutor createExecutor(final int maxParallelism, final long queueMaxHoldTime, final int flags) {
+ synchronized (PrioritisedThreadPool.this) {
+ if (PrioritisedThreadPool.this.shutdown) {
+ throw new IllegalStateException("Queue is shutdown: " + PrioritisedThreadPool.this.toString());
+ }
+
+ final ThreadPoolExecutor ret = new ThreadPoolExecutor(maxParallelism, queueMaxHoldTime, flags);
+
+ this.executors.add(ret);
+
+ return ret;
+ }
+ }
+
+ public final class ThreadPoolExecutor implements PrioritisedExecutor {
+
+ private final PrioritisedTaskQueue queue = new PrioritisedTaskQueue();
+
+ private volatile int maxParallelism;
+ private final long queueMaxHoldTime;
+ private volatile int currentParallelism;
+ private volatile boolean halt;
+ private long lastRetrieved = System.nanoTime();
+
+ private ThreadPoolExecutor(final int maxParallelism, final long queueMaxHoldTime, final int flags) {
+ this.maxParallelism = maxParallelism;
+ this.queueMaxHoldTime = queueMaxHoldTime;
+ }
+
+ private ExecutorGroup getGroup() {
+ return ExecutorGroup.this;
+ }
+
+ private boolean canNotify() {
+ if (this.halt) {
+ return false;
+ }
+
+ final int max = this.maxParallelism;
+ return max < 0 || this.currentParallelism < max;
+ }
+
+ private void notifyHighPriority() {
+ if (!this.canNotify()) {
+ return;
+ }
+ for (final PrioritisedThread thread : this.getGroup().getThreadPool().threads.getArray()) {
+ if (thread.alertHighPriorityExecutor()) {
+ return;
+ }
+ }
+ }
+
+ private void notifyScheduled() {
+ if (!this.canNotify()) {
+ return;
+ }
+ for (final PrioritisedThread thread : this.getGroup().getThreadPool().threads.getArray()) {
+ if (thread.notifyTasks()) {
+ return;
+ }
+ }
+ }
+
+ /**
+ * Removes this queue from the thread pool without shutting the queue down or waiting for queued tasks to be executed
+ */
+ public void halt() {
+ this.halt = true;
+
+ ExecutorGroup.this.executors.remove(this);
+ }
+
+ /**
+ * Returns whether this executor is scheduled to run tasks or is running tasks, otherwise it returns whether
+ * this queue is not halted and not shutdown.
+ */
+ public boolean isActive() {
+ if (this.halt) {
+ return this.currentParallelism > 0;
+ } else {
+ if (!this.isShutdown()) {
+ return true;
+ }
+
+ return !this.queue.hasNoScheduledTasks();
+ }
+ }
+
+ @Override
+ public boolean shutdown() {
+ if (!this.queue.shutdown()) {
+ return false;
+ }
+
+ if (this.queue.hasNoScheduledTasks()) {
+ ExecutorGroup.this.executors.remove(this);
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return this.queue.isShutdown();
+ }
+
+ public void setMaxParallelism(final int maxParallelism) {
+ this.maxParallelism = maxParallelism;
+ // assume that we could have increased the parallelism
+ if (this.getTargetPriority() != null) {
+ ExecutorGroup.this.getThreadPool().notifyAllThreads();
+ }
+ }
+
+ Priority getTargetPriority() {
+ final Priority ret = this.queue.getHighestPriority();
+ if (!this.isShutdown()) {
+ return ret;
+ }
+
+ return ret == null ? QUEUE_SHUTDOWN_PRIORITY : Priority.max(ret, QUEUE_SHUTDOWN_PRIORITY);
+ }
+
+ @Override
+ public long getTotalTasksScheduled() {
+ return this.queue.getTotalTasksScheduled();
+ }
+
+ @Override
+ public long getTotalTasksExecuted() {
+ return this.queue.getTotalTasksExecuted();
+ }
+
+ @Override
+ public long generateNextSubOrder() {
+ return ExecutorGroup.this.subOrderGenerator.getAndIncrement();
+ }
+
+ @Override
+ public boolean executeTask() {
+ return this.queue.executeTask();
+ }
+
+ @Override
+ public PrioritisedTask queueTask(final Runnable task) {
+ final PrioritisedTask ret = this.createTask(task);
+
+ ret.queue();
+
+ return ret;
+ }
+
+ @Override
+ public PrioritisedTask queueTask(final Runnable task, final Priority priority) {
+ final PrioritisedTask ret = this.createTask(task, priority);
+
+ ret.queue();
+
+ return ret;
+ }
+
+ @Override
+ public PrioritisedTask queueTask(final Runnable task, final Priority priority, final long subOrder) {
+ final PrioritisedTask ret = this.createTask(task, priority, subOrder);
+
+ ret.queue();
+
+ return ret;
+ }
+
+ @Override
+ public PrioritisedTask createTask(final Runnable task) {
+ return this.createTask(task, Priority.NORMAL);
+ }
+
+ @Override
+ public PrioritisedTask createTask(final Runnable task, final Priority priority) {
+ return this.createTask(task, priority, this.generateNextSubOrder());
+ }
+
+ @Override
+ public PrioritisedTask createTask(final Runnable task, final Priority priority, final long subOrder) {
+ return new WrappedTask(this.queue.createTask(task, priority, subOrder));
+ }
+
+ private final class WrappedTask implements PrioritisedTask {
+
+ private final PrioritisedTask wrapped;
+
+ private WrappedTask(final PrioritisedTask wrapped) {
+ this.wrapped = wrapped;
+ }
+
+ @Override
+ public PrioritisedExecutor getExecutor() {
+ return ThreadPoolExecutor.this;
+ }
+
+ @Override
+ public boolean queue() {
+ if (this.wrapped.queue()) {
+ final Priority priority = this.getPriority();
+ if (priority != Priority.COMPLETING) {
+ if (priority.isHigherOrEqualPriority(HIGH_PRIORITY_NOTIFY_THRESHOLD)) {
+ ThreadPoolExecutor.this.notifyHighPriority();
+ } else {
+ ThreadPoolExecutor.this.notifyScheduled();
+ }
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean isQueued() {
+ return this.wrapped.isQueued();
+ }
+
+ @Override
+ public boolean cancel() {
+ return this.wrapped.cancel();
+ }
+
+ @Override
+ public boolean execute() {
+ return this.wrapped.execute();
+ }
+
+ @Override
+ public Priority getPriority() {
+ return this.wrapped.getPriority();
+ }
+
+ @Override
+ public boolean setPriority(final Priority priority) {
+ if (this.wrapped.setPriority(priority)) {
+ if (priority.isHigherOrEqualPriority(HIGH_PRIORITY_NOTIFY_THRESHOLD)) {
+ ThreadPoolExecutor.this.notifyHighPriority();
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean raisePriority(final Priority priority) {
+ if (this.wrapped.raisePriority(priority)) {
+ if (priority.isHigherOrEqualPriority(HIGH_PRIORITY_NOTIFY_THRESHOLD)) {
+ ThreadPoolExecutor.this.notifyHighPriority();
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean lowerPriority(final Priority priority) {
+ return this.wrapped.lowerPriority(priority);
+ }
+
+ @Override
+ public long getSubOrder() {
+ return this.wrapped.getSubOrder();
+ }
+
+ @Override
+ public boolean setSubOrder(final long subOrder) {
+ return this.wrapped.setSubOrder(subOrder);
+ }
+
+ @Override
+ public boolean raiseSubOrder(final long subOrder) {
+ return this.wrapped.raiseSubOrder(subOrder);
+ }
+
+ @Override
+ public boolean lowerSubOrder(final long subOrder) {
+ return this.wrapped.lowerSubOrder(subOrder);
+ }
+
+ @Override
+ public boolean setPriorityAndSubOrder(final Priority priority, final long subOrder) {
+ if (this.wrapped.setPriorityAndSubOrder(priority, subOrder)) {
+ if (priority.isHigherOrEqualPriority(HIGH_PRIORITY_NOTIFY_THRESHOLD)) {
+ ThreadPoolExecutor.this.notifyHighPriority();
+ }
+ return true;
+ }
+
+ return false;
+ }
+ }
+ }
+ }
+
+ private static final class COWArrayList<E> {
+
+ private volatile E[] array;
+
+ public COWArrayList(final Class<E> clazz) {
+ this.array = (E[])Array.newInstance(clazz, 0);
+ }
+
+ public E[] getArray() {
+ return this.array;
+ }
+
+ public void add(final E element) {
+ synchronized (this) {
+ final E[] array = this.array;
+
+ final E[] copy = Arrays.copyOf(array, array.length + 1);
+ copy[array.length] = element;
+
+ this.array = copy;
+ }
+ }
+
+ public boolean remove(final E element) {
+ synchronized (this) {
+ final E[] array = this.array;
+ int index = -1;
+ for (int i = 0, len = array.length; i < len; ++i) {
+ if (array[i] == element) {
+ index = i;
+ break;
+ }
+ }
+
+ if (index == -1) {
+ return false;
+ }
+
+ final E[] copy = (E[])Array.newInstance(array.getClass().getComponentType(), array.length - 1);
+
+ System.arraycopy(array, 0, copy, 0, index);
+ System.arraycopy(array, index + 1, copy, index, (array.length - 1) - index);
+
+ this.array = copy;
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/src/main/java/ca/spottedleaf/concurrentutil/map/ConcurrentLong2ReferenceChainedHashTable.java b/src/main/java/ca/spottedleaf/concurrentutil/map/ConcurrentLong2ReferenceChainedHashTable.java
index d701998b376579ec652fb94823befa3cc0bc4090..6918f130099e6c19e20a47bfdb54915cdd13732a 100644
--- a/src/main/java/ca/spottedleaf/concurrentutil/map/ConcurrentLong2ReferenceChainedHashTable.java
+++ b/src/main/java/ca/spottedleaf/concurrentutil/map/ConcurrentLong2ReferenceChainedHashTable.java
@@ -43,7 +43,7 @@ import java.util.function.Predicate;
* @param <V>
* @see java.util.concurrent.ConcurrentMap
*/
-public class ConcurrentLong2ReferenceChainedHashTable<V> {
+public class ConcurrentLong2ReferenceChainedHashTable<V> implements Iterable<ConcurrentLong2ReferenceChainedHashTable.TableEntry<V>> {
protected static final int DEFAULT_CAPACITY = 16;
protected static final float DEFAULT_LOAD_FACTOR = 0.75f;
@@ -192,6 +192,7 @@ public class ConcurrentLong2ReferenceChainedHashTable<V> {
}
if (node.resize) {
+ // noinspection unchecked
table = (TableEntry<V>[])node.getValuePlain();
continue;
}
@@ -274,10 +275,10 @@ public class ConcurrentLong2ReferenceChainedHashTable<V> {
public int size() {
final long ret = this.size.sum();
- if (ret <= 0L) {
+ if (ret < 0L) {
return 0;
}
- if (ret >= (long)Integer.MAX_VALUE) {
+ if (ret > (long)Integer.MAX_VALUE) {
return Integer.MAX_VALUE;
}
@@ -341,6 +342,7 @@ public class ConcurrentLong2ReferenceChainedHashTable<V> {
// create new table data
+ // noinspection unchecked
final TableEntry<V>[] newTable = new TableEntry[capacity];
// noinspection unchecked
final TableEntry<V> resizeNode = new TableEntry<>(0L, (V)newTable, true);
@@ -365,6 +367,7 @@ public class ConcurrentLong2ReferenceChainedHashTable<V> {
throw new IllegalStateException("Resizing to same size");
}
+ // noinspection unchecked
final TableEntry<V>[] work = new TableEntry[1 << capDiffShift]; // typically, capDiffShift = 1
for (int i = 0, len = oldTable.length; i < len; ++i) {
@@ -538,6 +541,7 @@ public class ConcurrentLong2ReferenceChainedHashTable<V> {
}
if (node.resize) {
+ // noinspection unchecked
table = (TableEntry<V>[])node.getValuePlain();
continue table_loop;
}
@@ -599,6 +603,7 @@ public class ConcurrentLong2ReferenceChainedHashTable<V> {
}
if (node.resize) {
+ // noinspection unchecked
table = (TableEntry<V>[])node.getValuePlain();
continue table_loop;
}
@@ -653,6 +658,7 @@ public class ConcurrentLong2ReferenceChainedHashTable<V> {
}
if (node.resize) {
+ // noinspection unchecked
table = (TableEntry<V>[])node.getValuePlain();
continue table_loop;
}
@@ -704,6 +710,7 @@ public class ConcurrentLong2ReferenceChainedHashTable<V> {
}
if (node.resize) {
+ // noinspection unchecked
table = (TableEntry<V>[])node.getValuePlain();
continue table_loop;
}
@@ -772,6 +779,7 @@ public class ConcurrentLong2ReferenceChainedHashTable<V> {
}
if (node.resize) {
+ // noinspection unchecked
table = (TableEntry<V>[])node.getValuePlain();
continue table_loop;
}
@@ -848,6 +856,7 @@ public class ConcurrentLong2ReferenceChainedHashTable<V> {
}
if (node.resize) {
+ // noinspection unchecked
table = (TableEntry<V>[])node.getValuePlain();
continue table_loop;
}
@@ -944,6 +953,7 @@ public class ConcurrentLong2ReferenceChainedHashTable<V> {
}
if (node.resize) {
+ // noinspection unchecked
table = (TableEntry<V>[])node.getValuePlain();
continue table_loop;
}
@@ -1058,6 +1068,7 @@ public class ConcurrentLong2ReferenceChainedHashTable<V> {
}
if (node.resize) {
+ // noinspection unchecked
table = (TableEntry<V>[])node.getValuePlain();
continue table_loop;
}
@@ -1126,6 +1137,7 @@ public class ConcurrentLong2ReferenceChainedHashTable<V> {
}
if (node.resize) {
+ // noinspection unchecked
table = (TableEntry<V>[])node.getValuePlain();
continue table_loop;
}
@@ -1200,6 +1212,7 @@ public class ConcurrentLong2ReferenceChainedHashTable<V> {
}
if (node.resize) {
+ // noinspection unchecked
table = (TableEntry<V>[])node.getValuePlain();
continue table_loop;
}
@@ -1288,6 +1301,11 @@ public class ConcurrentLong2ReferenceChainedHashTable<V> {
return new EntryIterator<>(this);
}
+ @Override
+ public final Iterator<TableEntry<V>> iterator() {
+ return this.entryIterator();
+ }
+
/**
* Returns an iterator over the keys in this map. The iterator is only guaranteed to see keys that were
* added before the beginning of this call, but it may see keys added during.
@@ -1306,7 +1324,7 @@ public class ConcurrentLong2ReferenceChainedHashTable<V> {
protected static final class EntryIterator<V> extends BaseIteratorImpl<V, TableEntry<V>> {
- protected EntryIterator(final ConcurrentLong2ReferenceChainedHashTable<V> map) {
+ public EntryIterator(final ConcurrentLong2ReferenceChainedHashTable<V> map) {
super(map);
}
@@ -1326,7 +1344,7 @@ public class ConcurrentLong2ReferenceChainedHashTable<V> {
protected static final class KeyIterator<V> extends BaseIteratorImpl<V, Long> implements PrimitiveIterator.OfLong {
- protected KeyIterator(final ConcurrentLong2ReferenceChainedHashTable<V> map) {
+ public KeyIterator(final ConcurrentLong2ReferenceChainedHashTable<V> map) {
super(map);
}
@@ -1365,7 +1383,7 @@ public class ConcurrentLong2ReferenceChainedHashTable<V> {
protected static final class ValueIterator<V> extends BaseIteratorImpl<V, V> {
- protected ValueIterator(final ConcurrentLong2ReferenceChainedHashTable<V> map) {
+ public ValueIterator(final ConcurrentLong2ReferenceChainedHashTable<V> map) {
super(map);
}
@@ -1420,7 +1438,7 @@ public class ConcurrentLong2ReferenceChainedHashTable<V> {
@Override
public final void remove() {
- final TableEntry<V> lastReturned = this.nextToReturn;
+ final TableEntry<V> lastReturned = this.lastReturned;
if (lastReturned == null) {
throw new NoSuchElementException();
}
@@ -1492,6 +1510,7 @@ public class ConcurrentLong2ReferenceChainedHashTable<V> {
final ResizeChain<V> chain = this.resizeChain;
if (chain == null) {
+ // noinspection unchecked
final TableEntry<V>[] nextTable = (TableEntry<V>[])entry.getValuePlain();
final ResizeChain<V> oldChain = new ResizeChain<>(table, null, null);
@@ -1506,6 +1525,7 @@ public class ConcurrentLong2ReferenceChainedHashTable<V> {
} else {
ResizeChain<V> currChain = chain.next;
if (currChain == null) {
+ // noinspection unchecked
final TableEntry<V>[] ret = (TableEntry<V>[])entry.getValuePlain();
currChain = new ResizeChain<>(ret, chain, null);
chain.next = currChain;
@@ -1586,11 +1606,11 @@ public class ConcurrentLong2ReferenceChainedHashTable<V> {
protected static final class ResizeChain<V> {
- protected final TableEntry<V>[] table;
- protected final ResizeChain<V> prev;
- protected ResizeChain<V> next;
+ public final TableEntry<V>[] table;
+ public final ResizeChain<V> prev;
+ public ResizeChain<V> next;
- protected ResizeChain(final TableEntry<V>[] table, final ResizeChain<V> prev, final ResizeChain<V> next) {
+ public ResizeChain(final TableEntry<V>[] table, final ResizeChain<V> prev, final ResizeChain<V> next) {
this.table = table;
this.prev = prev;
this.next = next;
@@ -1600,64 +1620,64 @@ public class ConcurrentLong2ReferenceChainedHashTable<V> {
public static final class TableEntry<V> {
- protected static final VarHandle TABLE_ENTRY_ARRAY_HANDLE = ConcurrentUtil.getArrayHandle(TableEntry[].class);
+ private static final VarHandle TABLE_ENTRY_ARRAY_HANDLE = ConcurrentUtil.getArrayHandle(TableEntry[].class);
- protected final boolean resize;
+ private final boolean resize;
- protected final long key;
+ private final long key;
- protected volatile V value;
- protected static final VarHandle VALUE_HANDLE = ConcurrentUtil.getVarHandle(TableEntry.class, "value", Object.class);
+ private volatile V value;
+ private static final VarHandle VALUE_HANDLE = ConcurrentUtil.getVarHandle(TableEntry.class, "value", Object.class);
- protected final V getValuePlain() {
+ private V getValuePlain() {
//noinspection unchecked
return (V)VALUE_HANDLE.get(this);
}
- protected final V getValueAcquire() {
+ private V getValueAcquire() {
//noinspection unchecked
return (V)VALUE_HANDLE.getAcquire(this);
}
- protected final V getValueVolatile() {
+ private V getValueVolatile() {
//noinspection unchecked
return (V)VALUE_HANDLE.getVolatile(this);
}
- protected final void setValuePlain(final V value) {
+ private void setValuePlain(final V value) {
VALUE_HANDLE.set(this, (Object)value);
}
- protected final void setValueRelease(final V value) {
+ private void setValueRelease(final V value) {
VALUE_HANDLE.setRelease(this, (Object)value);
}
- protected final void setValueVolatile(final V value) {
+ private void setValueVolatile(final V value) {
VALUE_HANDLE.setVolatile(this, (Object)value);
}
- protected volatile TableEntry<V> next;
- protected static final VarHandle NEXT_HANDLE = ConcurrentUtil.getVarHandle(TableEntry.class, "next", TableEntry.class);
+ private volatile TableEntry<V> next;
+ private static final VarHandle NEXT_HANDLE = ConcurrentUtil.getVarHandle(TableEntry.class, "next", TableEntry.class);
- protected final TableEntry<V> getNextPlain() {
+ private TableEntry<V> getNextPlain() {
//noinspection unchecked
return (TableEntry<V>)NEXT_HANDLE.get(this);
}
- protected final TableEntry<V> getNextVolatile() {
+ private TableEntry<V> getNextVolatile() {
//noinspection unchecked
return (TableEntry<V>)NEXT_HANDLE.getVolatile(this);
}
- protected final void setNextPlain(final TableEntry<V> next) {
+ private void setNextPlain(final TableEntry<V> next) {
NEXT_HANDLE.set(this, next);
}
- protected final void setNextRelease(final TableEntry<V> next) {
+ private void setNextRelease(final TableEntry<V> next) {
NEXT_HANDLE.setRelease(this, next);
}
- protected final void setNextVolatile(final TableEntry<V> next) {
+ private void setNextVolatile(final TableEntry<V> next) {
NEXT_HANDLE.setVolatile(this, next);
}
diff --git a/src/main/java/ca/spottedleaf/concurrentutil/scheduler/SchedulerThreadPool.java b/src/main/java/ca/spottedleaf/concurrentutil/scheduler/SchedulerThreadPool.java
index 8197ccb1c4e5878dbd8007b5fb514640765ec8e4..85e6ef636d435a0ee4bf3e0760b0c87422c520a1 100644
--- a/src/main/java/ca/spottedleaf/concurrentutil/scheduler/SchedulerThreadPool.java
+++ b/src/main/java/ca/spottedleaf/concurrentutil/scheduler/SchedulerThreadPool.java
@@ -13,6 +13,10 @@ import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.LockSupport;
import java.util.function.BooleanSupplier;
+/**
+ * @deprecated To be replaced
+ */
+@Deprecated
public class SchedulerThreadPool {
public static final long DEADLINE_NOT_SET = Long.MIN_VALUE;
@@ -297,7 +301,9 @@ public class SchedulerThreadPool {
* is invoked for any scheduled task - otherwise, {@link #runTasks(BooleanSupplier)} may not be invoked to
* parse intermediate tasks.
* </p>
+ * @deprecated To be replaced
*/
+ @Deprecated
public static abstract class SchedulableTick {
private static final AtomicLong ID_GENERATOR = new AtomicLong();
public final long id = ID_GENERATOR.getAndIncrement();
diff --git a/src/main/java/ca/spottedleaf/concurrentutil/set/LinkedSortedSet.java b/src/main/java/ca/spottedleaf/concurrentutil/set/LinkedSortedSet.java
index 212bc9ae2fc7d37d4a089a2921b00de1e97f7cc1..82c4c11b0b564c97ac92bd5f54e3754a7ba95184 100644
--- a/src/main/java/ca/spottedleaf/concurrentutil/set/LinkedSortedSet.java
+++ b/src/main/java/ca/spottedleaf/concurrentutil/set/LinkedSortedSet.java
@@ -8,8 +8,8 @@ public final class LinkedSortedSet<E> implements Iterable<E> {
public final Comparator<? super E> comparator;
- protected Link<E> head;
- protected Link<E> tail;
+ private Link<E> head;
+ private Link<E> tail;
public LinkedSortedSet() {
this((Comparator)Comparator.naturalOrder());
@@ -257,8 +257,6 @@ public final class LinkedSortedSet<E> implements Iterable<E> {
private Link<E> prev;
private Link<E> next;
- private Link() {}
-
private Link(final E element) {
this.element = element;
}
diff --git a/src/main/java/ca/spottedleaf/concurrentutil/set/LinkedUnsortedList.java b/src/main/java/ca/spottedleaf/concurrentutil/set/LinkedUnsortedList.java
new file mode 100644
index 0000000000000000000000000000000000000000..bd8eb4f25d1dee00fbf9c05c14b0d94c5c641a55
--- /dev/null
+++ b/src/main/java/ca/spottedleaf/concurrentutil/set/LinkedUnsortedList.java
@@ -0,0 +1,204 @@
+package ca.spottedleaf.concurrentutil.set;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+
+public final class LinkedUnsortedList<E> implements Iterable<E> {
+
+ private Link<E> head;
+ private Link<E> tail;
+
+ public LinkedUnsortedList() {}
+
+ public void clear() {
+ this.head = this.tail = null;
+ }
+
+ public boolean isEmpty() {
+ return this.head == null;
+ }
+
+ public E first() {
+ final Link<E> head = this.head;
+ return head == null ? null : head.element;
+ }
+
+ public E last() {
+ final Link<E> tail = this.tail;
+ return tail == null ? null : tail.element;
+ }
+
+ public boolean containsFirst(final E element) {
+ for (Link<E> curr = this.head; curr != null; curr = curr.next) {
+ if (Objects.equals(element, curr.element)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean containsLast(final E element) {
+ for (Link<E> curr = this.tail; curr != null; curr = curr.prev) {
+ if (Objects.equals(element, curr.element)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void removeNode(final Link<E> node) {
+ final Link<E> prev = node.prev;
+ final Link<E> next = node.next;
+
+ // help GC
+ node.element = null;
+ node.prev = null;
+ node.next = null;
+
+ if (prev == null) {
+ this.head = next;
+ } else {
+ prev.next = next;
+ }
+
+ if (next == null) {
+ this.tail = prev;
+ } else {
+ next.prev = prev;
+ }
+ }
+
+ public boolean remove(final Link<E> link) {
+ if (link.element == null) {
+ return false;
+ }
+
+ this.removeNode(link);
+ return true;
+ }
+
+ public boolean removeFirst(final E element) {
+ for (Link<E> curr = this.head; curr != null; curr = curr.next) {
+ if (Objects.equals(element, curr.element)) {
+ this.removeNode(curr);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean removeLast(final E element) {
+ for (Link<E> curr = this.tail; curr != null; curr = curr.prev) {
+ if (Objects.equals(element, curr.element)) {
+ this.removeNode(curr);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Iterator<E> iterator() {
+ return new Iterator<>() {
+ private Link<E> next = LinkedUnsortedList.this.head;
+
+ @Override
+ public boolean hasNext() {
+ return this.next != null;
+ }
+
+ @Override
+ public E next() {
+ final Link<E> next = this.next;
+ if (next == null) {
+ throw new NoSuchElementException();
+ }
+ this.next = next.next;
+ return next.element;
+ }
+ };
+ }
+
+ public E pollFirst() {
+ final Link<E> head = this.head;
+ if (head == null) {
+ return null;
+ }
+
+ final E ret = head.element;
+ final Link<E> next = head.next;
+
+ // unlink head
+ this.head = next;
+ if (next == null) {
+ this.tail = null;
+ } else {
+ next.prev = null;
+ }
+
+ // help GC
+ head.element = null;
+ head.next = null;
+
+ return ret;
+ }
+
+ public E pollLast() {
+ final Link<E> tail = this.tail;
+ if (tail == null) {
+ return null;
+ }
+
+ final E ret = tail.element;
+ final Link<E> prev = tail.prev;
+
+ // unlink tail
+ this.tail = prev;
+ if (prev == null) {
+ this.head = null;
+ } else {
+ prev.next = null;
+ }
+
+ // help GC
+ tail.element = null;
+ tail.prev = null;
+
+ return ret;
+ }
+
+ public Link<E> addLast(final E element) {
+ final Link<E> curr = this.tail;
+ if (curr != null) {
+ return this.tail = new Link<>(element, curr, null);
+ } else {
+ return this.head = this.tail = new Link<>(element);
+ }
+ }
+
+ public Link<E> addFirst(final E element) {
+ final Link<E> curr = this.head;
+ if (curr != null) {
+ return this.head = new Link<>(element, null, curr);
+ } else {
+ return this.head = this.tail = new Link<>(element);
+ }
+ }
+
+ public static final class Link<E> {
+ private E element;
+ private Link<E> prev;
+ private Link<E> next;
+
+ private Link(final E element) {
+ this.element = element;
+ }
+
+ private Link(final E element, final Link<E> prev, final Link<E> next) {
+ this.element = element;
+ this.prev = prev;
+ this.next = next;
+ }
+ }
+}
diff --git a/src/main/java/ca/spottedleaf/concurrentutil/util/ArrayUtil.java b/src/main/java/ca/spottedleaf/concurrentutil/util/ArrayUtil.java
deleted file mode 100644
index ebb1ab06165addb173fea4d295001fe37f4e79d3..0000000000000000000000000000000000000000
--- a/src/main/java/ca/spottedleaf/concurrentutil/util/ArrayUtil.java
+++ /dev/null
@@ -1,816 +0,0 @@
-package ca.spottedleaf.concurrentutil.util;
-
-import java.lang.invoke.VarHandle;
-
-public final class ArrayUtil {
-
- public static final VarHandle BOOLEAN_ARRAY_HANDLE = ConcurrentUtil.getArrayHandle(boolean[].class);
-
- public static final VarHandle BYTE_ARRAY_HANDLE = ConcurrentUtil.getArrayHandle(byte[].class);
-
- public static final VarHandle SHORT_ARRAY_HANDLE = ConcurrentUtil.getArrayHandle(short[].class);
-
- public static final VarHandle INT_ARRAY_HANDLE = ConcurrentUtil.getArrayHandle(int[].class);
-
- public static final VarHandle LONG_ARRAY_HANDLE = ConcurrentUtil.getArrayHandle(long[].class);
-
- public static final VarHandle OBJECT_ARRAY_HANDLE = ConcurrentUtil.getArrayHandle(Object[].class);
-
- private ArrayUtil() {
- throw new RuntimeException();
- }
-
- /* byte array */
-
- public static byte getPlain(final byte[] array, final int index) {
- return (byte)BYTE_ARRAY_HANDLE.get(array, index);
- }
-
- public static byte getOpaque(final byte[] array, final int index) {
- return (byte)BYTE_ARRAY_HANDLE.getOpaque(array, index);
- }
-
- public static byte getAcquire(final byte[] array, final int index) {
- return (byte)BYTE_ARRAY_HANDLE.getAcquire(array, index);
- }
-
- public static byte getVolatile(final byte[] array, final int index) {
- return (byte)BYTE_ARRAY_HANDLE.getVolatile(array, index);
- }
-
- public static void setPlain(final byte[] array, final int index, final byte value) {
- BYTE_ARRAY_HANDLE.set(array, index, value);
- }
-
- public static void setOpaque(final byte[] array, final int index, final byte value) {
- BYTE_ARRAY_HANDLE.setOpaque(array, index, value);
- }
-
- public static void setRelease(final byte[] array, final int index, final byte value) {
- BYTE_ARRAY_HANDLE.setRelease(array, index, value);
- }
-
- public static void setVolatile(final byte[] array, final int index, final byte value) {
- BYTE_ARRAY_HANDLE.setVolatile(array, index, value);
- }
-
- public static void setVolatileContended(final byte[] array, final int index, final byte param) {
- int failures = 0;
-
- for (byte curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, param))) {
- return;
- }
- }
- }
-
- public static byte compareAndExchangeVolatile(final byte[] array, final int index, final byte expect, final byte update) {
- return (byte)BYTE_ARRAY_HANDLE.compareAndExchange(array, index, expect, update);
- }
-
- public static byte getAndAddVolatile(final byte[] array, final int index, final byte param) {
- return (byte)BYTE_ARRAY_HANDLE.getAndAdd(array, index, param);
- }
-
- public static byte getAndAndVolatile(final byte[] array, final int index, final byte param) {
- return (byte)BYTE_ARRAY_HANDLE.getAndBitwiseAnd(array, index, param);
- }
-
- public static byte getAndOrVolatile(final byte[] array, final int index, final byte param) {
- return (byte)BYTE_ARRAY_HANDLE.getAndBitwiseOr(array, index, param);
- }
-
- public static byte getAndXorVolatile(final byte[] array, final int index, final byte param) {
- return (byte)BYTE_ARRAY_HANDLE.getAndBitwiseXor(array, index, param);
- }
-
- public static byte getAndSetVolatile(final byte[] array, final int index, final byte param) {
- return (byte)BYTE_ARRAY_HANDLE.getAndSet(array, index, param);
- }
-
- public static byte compareAndExchangeVolatileContended(final byte[] array, final int index, final byte expect, final byte update) {
- return (byte)BYTE_ARRAY_HANDLE.compareAndExchange(array, index, expect, update);
- }
-
- public static byte getAndAddVolatileContended(final byte[] array, final int index, final byte param) {
- int failures = 0;
-
- for (byte curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, (byte) (curr + param)))) {
- return curr;
- }
- }
- }
-
- public static byte getAndAndVolatileContended(final byte[] array, final int index, final byte param) {
- int failures = 0;
-
- for (byte curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, (byte) (curr & param)))) {
- return curr;
- }
- }
- }
-
- public static byte getAndOrVolatileContended(final byte[] array, final int index, final byte param) {
- int failures = 0;
-
- for (byte curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, (byte) (curr | param)))) {
- return curr;
- }
- }
- }
-
- public static byte getAndXorVolatileContended(final byte[] array, final int index, final byte param) {
- int failures = 0;
-
- for (byte curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, (byte) (curr ^ param)))) {
- return curr;
- }
- }
- }
-
- public static byte getAndSetVolatileContended(final byte[] array, final int index, final byte param) {
- int failures = 0;
-
- for (byte curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, param))) {
- return curr;
- }
- }
- }
-
- /* short array */
-
- public static short getPlain(final short[] array, final int index) {
- return (short)SHORT_ARRAY_HANDLE.get(array, index);
- }
-
- public static short getOpaque(final short[] array, final int index) {
- return (short)SHORT_ARRAY_HANDLE.getOpaque(array, index);
- }
-
- public static short getAcquire(final short[] array, final int index) {
- return (short)SHORT_ARRAY_HANDLE.getAcquire(array, index);
- }
-
- public static short getVolatile(final short[] array, final int index) {
- return (short)SHORT_ARRAY_HANDLE.getVolatile(array, index);
- }
-
- public static void setPlain(final short[] array, final int index, final short value) {
- SHORT_ARRAY_HANDLE.set(array, index, value);
- }
-
- public static void setOpaque(final short[] array, final int index, final short value) {
- SHORT_ARRAY_HANDLE.setOpaque(array, index, value);
- }
-
- public static void setRelease(final short[] array, final int index, final short value) {
- SHORT_ARRAY_HANDLE.setRelease(array, index, value);
- }
-
- public static void setVolatile(final short[] array, final int index, final short value) {
- SHORT_ARRAY_HANDLE.setVolatile(array, index, value);
- }
-
- public static void setVolatileContended(final short[] array, final int index, final short param) {
- int failures = 0;
-
- for (short curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, param))) {
- return;
- }
- }
- }
-
- public static short compareAndExchangeVolatile(final short[] array, final int index, final short expect, final short update) {
- return (short)SHORT_ARRAY_HANDLE.compareAndExchange(array, index, expect, update);
- }
-
- public static short getAndAddVolatile(final short[] array, final int index, final short param) {
- return (short)SHORT_ARRAY_HANDLE.getAndAdd(array, index, param);
- }
-
- public static short getAndAndVolatile(final short[] array, final int index, final short param) {
- return (short)SHORT_ARRAY_HANDLE.getAndBitwiseAnd(array, index, param);
- }
-
- public static short getAndOrVolatile(final short[] array, final int index, final short param) {
- return (short)SHORT_ARRAY_HANDLE.getAndBitwiseOr(array, index, param);
- }
-
- public static short getAndXorVolatile(final short[] array, final int index, final short param) {
- return (short)SHORT_ARRAY_HANDLE.getAndBitwiseXor(array, index, param);
- }
-
- public static short getAndSetVolatile(final short[] array, final int index, final short param) {
- return (short)SHORT_ARRAY_HANDLE.getAndSet(array, index, param);
- }
-
- public static short compareAndExchangeVolatileContended(final short[] array, final int index, final short expect, final short update) {
- return (short)SHORT_ARRAY_HANDLE.compareAndExchange(array, index, expect, update);
- }
-
- public static short getAndAddVolatileContended(final short[] array, final int index, final short param) {
- int failures = 0;
-
- for (short curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, (short) (curr + param)))) {
- return curr;
- }
- }
- }
-
- public static short getAndAndVolatileContended(final short[] array, final int index, final short param) {
- int failures = 0;
-
- for (short curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, (short) (curr & param)))) {
- return curr;
- }
- }
- }
-
- public static short getAndOrVolatileContended(final short[] array, final int index, final short param) {
- int failures = 0;
-
- for (short curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, (short) (curr | param)))) {
- return curr;
- }
- }
- }
-
- public static short getAndXorVolatileContended(final short[] array, final int index, final short param) {
- int failures = 0;
-
- for (short curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, (short) (curr ^ param)))) {
- return curr;
- }
- }
- }
-
- public static short getAndSetVolatileContended(final short[] array, final int index, final short param) {
- int failures = 0;
-
- for (short curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, param))) {
- return curr;
- }
- }
- }
-
- /* int array */
-
- public static int getPlain(final int[] array, final int index) {
- return (int)INT_ARRAY_HANDLE.get(array, index);
- }
-
- public static int getOpaque(final int[] array, final int index) {
- return (int)INT_ARRAY_HANDLE.getOpaque(array, index);
- }
-
- public static int getAcquire(final int[] array, final int index) {
- return (int)INT_ARRAY_HANDLE.getAcquire(array, index);
- }
-
- public static int getVolatile(final int[] array, final int index) {
- return (int)INT_ARRAY_HANDLE.getVolatile(array, index);
- }
-
- public static void setPlain(final int[] array, final int index, final int value) {
- INT_ARRAY_HANDLE.set(array, index, value);
- }
-
- public static void setOpaque(final int[] array, final int index, final int value) {
- INT_ARRAY_HANDLE.setOpaque(array, index, value);
- }
-
- public static void setRelease(final int[] array, final int index, final int value) {
- INT_ARRAY_HANDLE.setRelease(array, index, value);
- }
-
- public static void setVolatile(final int[] array, final int index, final int value) {
- INT_ARRAY_HANDLE.setVolatile(array, index, value);
- }
-
- public static void setVolatileContended(final int[] array, final int index, final int param) {
- int failures = 0;
-
- for (int curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, param))) {
- return;
- }
- }
- }
-
- public static int compareAndExchangeVolatile(final int[] array, final int index, final int expect, final int update) {
- return (int)INT_ARRAY_HANDLE.compareAndExchange(array, index, expect, update);
- }
-
- public static int getAndAddVolatile(final int[] array, final int index, final int param) {
- return (int)INT_ARRAY_HANDLE.getAndAdd(array, index, param);
- }
-
- public static int getAndAndVolatile(final int[] array, final int index, final int param) {
- return (int)INT_ARRAY_HANDLE.getAndBitwiseAnd(array, index, param);
- }
-
- public static int getAndOrVolatile(final int[] array, final int index, final int param) {
- return (int)INT_ARRAY_HANDLE.getAndBitwiseOr(array, index, param);
- }
-
- public static int getAndXorVolatile(final int[] array, final int index, final int param) {
- return (int)INT_ARRAY_HANDLE.getAndBitwiseXor(array, index, param);
- }
-
- public static int getAndSetVolatile(final int[] array, final int index, final int param) {
- return (int)INT_ARRAY_HANDLE.getAndSet(array, index, param);
- }
-
- public static int compareAndExchangeVolatileContended(final int[] array, final int index, final int expect, final int update) {
- return (int)INT_ARRAY_HANDLE.compareAndExchange(array, index, expect, update);
- }
-
- public static int getAndAddVolatileContended(final int[] array, final int index, final int param) {
- int failures = 0;
-
- for (int curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, (int) (curr + param)))) {
- return curr;
- }
- }
- }
-
- public static int getAndAndVolatileContended(final int[] array, final int index, final int param) {
- int failures = 0;
-
- for (int curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, (int) (curr & param)))) {
- return curr;
- }
- }
- }
-
- public static int getAndOrVolatileContended(final int[] array, final int index, final int param) {
- int failures = 0;
-
- for (int curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, (int) (curr | param)))) {
- return curr;
- }
- }
- }
-
- public static int getAndXorVolatileContended(final int[] array, final int index, final int param) {
- int failures = 0;
-
- for (int curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, (int) (curr ^ param)))) {
- return curr;
- }
- }
- }
-
- public static int getAndSetVolatileContended(final int[] array, final int index, final int param) {
- int failures = 0;
-
- for (int curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, param))) {
- return curr;
- }
- }
- }
-
- /* long array */
-
- public static long getPlain(final long[] array, final int index) {
- return (long)LONG_ARRAY_HANDLE.get(array, index);
- }
-
- public static long getOpaque(final long[] array, final int index) {
- return (long)LONG_ARRAY_HANDLE.getOpaque(array, index);
- }
-
- public static long getAcquire(final long[] array, final int index) {
- return (long)LONG_ARRAY_HANDLE.getAcquire(array, index);
- }
-
- public static long getVolatile(final long[] array, final int index) {
- return (long)LONG_ARRAY_HANDLE.getVolatile(array, index);
- }
-
- public static void setPlain(final long[] array, final int index, final long value) {
- LONG_ARRAY_HANDLE.set(array, index, value);
- }
-
- public static void setOpaque(final long[] array, final int index, final long value) {
- LONG_ARRAY_HANDLE.setOpaque(array, index, value);
- }
-
- public static void setRelease(final long[] array, final int index, final long value) {
- LONG_ARRAY_HANDLE.setRelease(array, index, value);
- }
-
- public static void setVolatile(final long[] array, final int index, final long value) {
- LONG_ARRAY_HANDLE.setVolatile(array, index, value);
- }
-
- public static void setVolatileContended(final long[] array, final int index, final long param) {
- int failures = 0;
-
- for (long curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, param))) {
- return;
- }
- }
- }
-
- public static long compareAndExchangeVolatile(final long[] array, final int index, final long expect, final long update) {
- return (long)LONG_ARRAY_HANDLE.compareAndExchange(array, index, expect, update);
- }
-
- public static long getAndAddVolatile(final long[] array, final int index, final long param) {
- return (long)LONG_ARRAY_HANDLE.getAndAdd(array, index, param);
- }
-
- public static long getAndAndVolatile(final long[] array, final int index, final long param) {
- return (long)LONG_ARRAY_HANDLE.getAndBitwiseAnd(array, index, param);
- }
-
- public static long getAndOrVolatile(final long[] array, final int index, final long param) {
- return (long)LONG_ARRAY_HANDLE.getAndBitwiseOr(array, index, param);
- }
-
- public static long getAndXorVolatile(final long[] array, final int index, final long param) {
- return (long)LONG_ARRAY_HANDLE.getAndBitwiseXor(array, index, param);
- }
-
- public static long getAndSetVolatile(final long[] array, final int index, final long param) {
- return (long)LONG_ARRAY_HANDLE.getAndSet(array, index, param);
- }
-
- public static long compareAndExchangeVolatileContended(final long[] array, final int index, final long expect, final long update) {
- return (long)LONG_ARRAY_HANDLE.compareAndExchange(array, index, expect, update);
- }
-
- public static long getAndAddVolatileContended(final long[] array, final int index, final long param) {
- int failures = 0;
-
- for (long curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, (long) (curr + param)))) {
- return curr;
- }
- }
- }
-
- public static long getAndAndVolatileContended(final long[] array, final int index, final long param) {
- int failures = 0;
-
- for (long curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, (long) (curr & param)))) {
- return curr;
- }
- }
- }
-
- public static long getAndOrVolatileContended(final long[] array, final int index, final long param) {
- int failures = 0;
-
- for (long curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, (long) (curr | param)))) {
- return curr;
- }
- }
- }
-
- public static long getAndXorVolatileContended(final long[] array, final int index, final long param) {
- int failures = 0;
-
- for (long curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, (long) (curr ^ param)))) {
- return curr;
- }
- }
- }
-
- public static long getAndSetVolatileContended(final long[] array, final int index, final long param) {
- int failures = 0;
-
- for (long curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, param))) {
- return curr;
- }
- }
- }
-
- /* boolean array */
-
- public static boolean getPlain(final boolean[] array, final int index) {
- return (boolean)BOOLEAN_ARRAY_HANDLE.get(array, index);
- }
-
- public static boolean getOpaque(final boolean[] array, final int index) {
- return (boolean)BOOLEAN_ARRAY_HANDLE.getOpaque(array, index);
- }
-
- public static boolean getAcquire(final boolean[] array, final int index) {
- return (boolean)BOOLEAN_ARRAY_HANDLE.getAcquire(array, index);
- }
-
- public static boolean getVolatile(final boolean[] array, final int index) {
- return (boolean)BOOLEAN_ARRAY_HANDLE.getVolatile(array, index);
- }
-
- public static void setPlain(final boolean[] array, final int index, final boolean value) {
- BOOLEAN_ARRAY_HANDLE.set(array, index, value);
- }
-
- public static void setOpaque(final boolean[] array, final int index, final boolean value) {
- BOOLEAN_ARRAY_HANDLE.setOpaque(array, index, value);
- }
-
- public static void setRelease(final boolean[] array, final int index, final boolean value) {
- BOOLEAN_ARRAY_HANDLE.setRelease(array, index, value);
- }
-
- public static void setVolatile(final boolean[] array, final int index, final boolean value) {
- BOOLEAN_ARRAY_HANDLE.setVolatile(array, index, value);
- }
-
- public static void setVolatileContended(final boolean[] array, final int index, final boolean param) {
- int failures = 0;
-
- for (boolean curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, param))) {
- return;
- }
- }
- }
-
- public static boolean compareAndExchangeVolatile(final boolean[] array, final int index, final boolean expect, final boolean update) {
- return (boolean)BOOLEAN_ARRAY_HANDLE.compareAndExchange(array, index, expect, update);
- }
-
- public static boolean getAndOrVolatile(final boolean[] array, final int index, final boolean param) {
- return (boolean)BOOLEAN_ARRAY_HANDLE.getAndBitwiseOr(array, index, param);
- }
-
- public static boolean getAndXorVolatile(final boolean[] array, final int index, final boolean param) {
- return (boolean)BOOLEAN_ARRAY_HANDLE.getAndBitwiseXor(array, index, param);
- }
-
- public static boolean getAndSetVolatile(final boolean[] array, final int index, final boolean param) {
- return (boolean)BOOLEAN_ARRAY_HANDLE.getAndSet(array, index, param);
- }
-
- public static boolean compareAndExchangeVolatileContended(final boolean[] array, final int index, final boolean expect, final boolean update) {
- return (boolean)BOOLEAN_ARRAY_HANDLE.compareAndExchange(array, index, expect, update);
- }
-
- public static boolean getAndAndVolatileContended(final boolean[] array, final int index, final boolean param) {
- int failures = 0;
-
- for (boolean curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, (boolean) (curr & param)))) {
- return curr;
- }
- }
- }
-
- public static boolean getAndOrVolatileContended(final boolean[] array, final int index, final boolean param) {
- int failures = 0;
-
- for (boolean curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, (boolean) (curr | param)))) {
- return curr;
- }
- }
- }
-
- public static boolean getAndXorVolatileContended(final boolean[] array, final int index, final boolean param) {
- int failures = 0;
-
- for (boolean curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, (boolean) (curr ^ param)))) {
- return curr;
- }
- }
- }
-
- public static boolean getAndSetVolatileContended(final boolean[] array, final int index, final boolean param) {
- int failures = 0;
-
- for (boolean curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, param))) {
- return curr;
- }
- }
- }
-
- @SuppressWarnings("unchecked")
- public static <T> T getPlain(final T[] array, final int index) {
- final Object ret = OBJECT_ARRAY_HANDLE.get((Object[])array, index);
- return (T)ret;
- }
-
- @SuppressWarnings("unchecked")
- public static <T> T getOpaque(final T[] array, final int index) {
- final Object ret = OBJECT_ARRAY_HANDLE.getOpaque((Object[])array, index);
- return (T)ret;
- }
-
- @SuppressWarnings("unchecked")
- public static <T> T getAcquire(final T[] array, final int index) {
- final Object ret = OBJECT_ARRAY_HANDLE.getAcquire((Object[])array, index);
- return (T)ret;
- }
-
- @SuppressWarnings("unchecked")
- public static <T> T getVolatile(final T[] array, final int index) {
- final Object ret = OBJECT_ARRAY_HANDLE.getVolatile((Object[])array, index);
- return (T)ret;
- }
-
- public static <T> void setPlain(final T[] array, final int index, final T value) {
- OBJECT_ARRAY_HANDLE.set((Object[])array, index, (Object)value);
- }
-
- public static <T> void setOpaque(final T[] array, final int index, final T value) {
- OBJECT_ARRAY_HANDLE.setOpaque((Object[])array, index, (Object)value);
- }
-
- public static <T> void setRelease(final T[] array, final int index, final T value) {
- OBJECT_ARRAY_HANDLE.setRelease((Object[])array, index, (Object)value);
- }
-
- public static <T> void setVolatile(final T[] array, final int index, final T value) {
- OBJECT_ARRAY_HANDLE.setVolatile((Object[])array, index, (Object)value);
- }
-
- public static <T> void setVolatileContended(final T[] array, final int index, final T param) {
- int failures = 0;
-
- for (T curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, param))) {
- return;
- }
- }
- }
-
- @SuppressWarnings("unchecked")
- public static <T> T compareAndExchangeVolatile(final T[] array, final int index, final T expect, final T update) {
- final Object ret = OBJECT_ARRAY_HANDLE.compareAndExchange((Object[])array, index, (Object)expect, (Object)update);
- return (T)ret;
- }
-
- @SuppressWarnings("unchecked")
- public static <T> T getAndSetVolatile(final T[] array, final int index, final T param) {
- final Object ret = BYTE_ARRAY_HANDLE.getAndSet((Object[])array, index, (Object)param);
- return (T)ret;
- }
-
- @SuppressWarnings("unchecked")
- public static <T> T compareAndExchangeVolatileContended(final T[] array, final int index, final T expect, final T update) {
- final Object ret = OBJECT_ARRAY_HANDLE.compareAndExchange((Object[])array, index, (Object)expect, (Object)update);
- return (T)ret;
- }
-
- public static <T> T getAndSetVolatileContended(final T[] array, final int index, final T param) {
- int failures = 0;
-
- for (T curr = getVolatile(array, index);;++failures) {
- for (int i = 0; i < failures; ++i) {
- ConcurrentUtil.backoff();
- }
-
- if (curr == (curr = compareAndExchangeVolatileContended(array, index, curr, param))) {
- return curr;
- }
- }
- }
-}
diff --git a/src/main/java/ca/spottedleaf/concurrentutil/util/IntegerUtil.java b/src/main/java/ca/spottedleaf/concurrentutil/util/IntegerUtil.java
index 77699c5fa9681f9ec7aa05cbb50eb60828e193ab..9d7b9b8158cd01d12adbd7896ff77bee9828e101 100644
--- a/src/main/java/ca/spottedleaf/concurrentutil/util/IntegerUtil.java
+++ b/src/main/java/ca/spottedleaf/concurrentutil/util/IntegerUtil.java
@@ -170,6 +170,26 @@ public final class IntegerUtil {
return (mask ^ val) - mask; // if val < 0, then (0 ^ val) - 0 else (-1 ^ val) + 1
}
+ // https://lemire.me/blog/2019/02/08/faster-remainders-when-the-divisor-is-a-constant-beating-compilers-and-libdivide
+ /**
+ *
+ * Usage:
+ * <pre>
+ * {@code
+ * static final long mult = getSimpleMultiplier(divisor, bits);
+ * long x = ...;
+ * long magic = x * mult;
+ * long divQ = magic >>> bits;
+ * long divR = ((magic & ((1 << bits) - 1)) * divisor) >>> bits;
+ * }
+ * </pre>
+ *
+ * @param bits The number of bits of precision for the returned result
+ */
+ public static long getUnsignedDivisorMagic(final long divisor, final int bits) {
+ return (((1L << bits) - 1L) / divisor) + 1;
+ }
+
private IntegerUtil() {
throw new RuntimeException();
}
diff --git a/src/main/java/ca/spottedleaf/concurrentutil/util/Priority.java b/src/main/java/ca/spottedleaf/concurrentutil/util/Priority.java
new file mode 100644
index 0000000000000000000000000000000000000000..2919bbaa07b70f182438c3be8f9ebbe0649809b6
--- /dev/null
+++ b/src/main/java/ca/spottedleaf/concurrentutil/util/Priority.java
@@ -0,0 +1,145 @@
+package ca.spottedleaf.concurrentutil.util;
+
+public enum Priority {
+
+ /**
+ * Priority value indicating the task has completed or is being completed.
+ * This priority cannot be used to schedule tasks.
+ */
+ COMPLETING(-1),
+
+ /**
+ * Absolute highest priority, should only be used for when a task is blocking a time-critical thread.
+ */
+ BLOCKING(),
+
+ /**
+ * Should only be used for urgent but not time-critical tasks.
+ */
+ HIGHEST(),
+
+ /**
+ * Two priorities above normal.
+ */
+ HIGHER(),
+
+ /**
+ * One priority above normal.
+ */
+ HIGH(),
+
+ /**
+ * Default priority.
+ */
+ NORMAL(),
+
+ /**
+ * One priority below normal.
+ */
+ LOW(),
+
+ /**
+ * Two priorities below normal.
+ */
+ LOWER(),
+
+ /**
+ * Use for tasks that should eventually execute, but are not needed to.
+ */
+ LOWEST(),
+
+ /**
+ * Use for tasks that can be delayed indefinitely.
+ */
+ IDLE();
+
+ // returns whether the priority can be scheduled
+ public static boolean isValidPriority(final Priority priority) {
+ return priority != null && priority != priority.COMPLETING;
+ }
+
+ // returns the higher priority of the two
+ public static Priority max(final Priority p1, final Priority p2) {
+ return p1.isHigherOrEqualPriority(p2) ? p1 : p2;
+ }
+
+ // returns the lower priroity of the two
+ public static Priority min(final Priority p1, final Priority p2) {
+ return p1.isLowerOrEqualPriority(p2) ? p1 : p2;
+ }
+
+ public boolean isHigherOrEqualPriority(final Priority than) {
+ return this.priority <= than.priority;
+ }
+
+ public boolean isHigherPriority(final Priority than) {
+ return this.priority < than.priority;
+ }
+
+ public boolean isLowerOrEqualPriority(final Priority than) {
+ return this.priority >= than.priority;
+ }
+
+ public boolean isLowerPriority(final Priority than) {
+ return this.priority > than.priority;
+ }
+
+ public boolean isHigherOrEqualPriority(final int than) {
+ return this.priority <= than;
+ }
+
+ public boolean isHigherPriority(final int than) {
+ return this.priority < than;
+ }
+
+ public boolean isLowerOrEqualPriority(final int than) {
+ return this.priority >= than;
+ }
+
+ public boolean isLowerPriority(final int than) {
+ return this.priority > than;
+ }
+
+ public static boolean isHigherOrEqualPriority(final int priority, final int than) {
+ return priority <= than;
+ }
+
+ public static boolean isHigherPriority(final int priority, final int than) {
+ return priority < than;
+ }
+
+ public static boolean isLowerOrEqualPriority(final int priority, final int than) {
+ return priority >= than;
+ }
+
+ public static boolean isLowerPriority(final int priority, final int than) {
+ return priority > than;
+ }
+
+ static final Priority[] PRIORITIES = Priority.values();
+
+ /** includes special priorities */
+ public static final int TOTAL_PRIORITIES = PRIORITIES.length;
+
+ public static final int TOTAL_SCHEDULABLE_PRIORITIES = TOTAL_PRIORITIES - 1;
+
+ public static Priority getPriority(final int priority) {
+ return PRIORITIES[priority + 1];
+ }
+
+ private static int priorityCounter;
+
+ private static int nextCounter() {
+ return priorityCounter++;
+ }
+
+ public final int priority;
+
+ private Priority() {
+ this(nextCounter());
+ }
+
+ private Priority(final int priority) {
+ this.priority = priority;
+ }
+}
\ No newline at end of file