mirror of
https://github.com/PaperMC/Paper.git
synced 2024-11-22 18:45:54 +01:00
6178 lines
229 KiB
Diff
6178 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
|