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

io.github.vipcxj.jasync.ng.runtime.concurrent.AbstractJasyncQueuedSynchronizer Maven / Gradle / Ivy

package io.github.vipcxj.jasync.ng.runtime.concurrent;

import io.github.vipcxj.jasync.ng.spec.JAsyncRoutine;
import io.github.vipcxj.schedule.EventHandle;
import io.github.vipcxj.schedule.Schedule;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Lock;

@SuppressWarnings({"unused", "UnusedReturnValue"})
public class AbstractJasyncQueuedSynchronizer implements java.io.Serializable {

    private static final long serialVersionUID = 5344653732113768596L;

    protected AbstractJasyncQueuedSynchronizer() { }

    // RoutineNode status bits, also used as argument and return values
    static final int WAITING   = 1;          // must be 1
    static final int BUSY      = 2;
    static final int BUSY_TO_REMOVING_THEN_CANCEL = -1;
    static final int BUSY_TO_REMOVING_THEN_RESUME = -2;
    static final int REMOVING_THEN_CANCEL = -3;
    static final int REMOVING_THEN_RESUME = -4;
    static final int REMOVED   = -3;
    static final int COND      = 3;          // in a condition wait

    private static final Schedule SCHEDULE = Schedule.instance();

    static abstract class Node> {
        abstract boolean weakCasNext(N c, N v);
        abstract N getNext();
    }

    static class HeadNode extends Node {
        volatile RoutineNode next = TAIL_NEXT;       // visibly nonnull when signallable
        static final AtomicReferenceFieldUpdater NEXT = AtomicReferenceFieldUpdater.newUpdater(HeadNode.class, RoutineNode.class, "next");

        @Override
        public RoutineNode getNext() {
            return next;
        }

        @Override
        final boolean weakCasNext(RoutineNode c, RoutineNode v) {  // for cleanQueue
            return NEXT.weakCompareAndSet(this, c, v);
        }
    }

    /** CLH Nodes */
    static class RoutineNode extends Node {
        // TAIL_NEXT -> some node -> null
        // When enqueued: TAIL_NEXT
        // When some node enqueued after: some node
        // When dequeued: null
        volatile RoutineNode next;       // visibly nonnull when signallable
        static final AtomicReferenceFieldUpdater NEXT = AtomicReferenceFieldUpdater.newUpdater(RoutineNode.class, RoutineNode.class, "next");
        boolean interruptible;
        Waiter waiter;            // visibly nonnull when enqueued
        volatile EventHandle handle;
        // BUSY <-> WAITING
        // WAITING -> REMOVING_THEN_CANCEL -> REMOVED
        // WAITING -> REMOVING_THEN_RESUME -> REMOVED
        // BUSY -> BUSY_TO_REMOVING_THEN_CANCEL -> REMOVED
        // BUSY -> BUSY_TO_REMOVING_THEN_RESUME -> REMOVED
        volatile int status;
        static final AtomicIntegerFieldUpdater STATUS = AtomicIntegerFieldUpdater.newUpdater(RoutineNode.class, "status");

        RoutineNode(Waiter waiter, boolean interruptible) {
            this.waiter = waiter;
            this.interruptible = interruptible;
            this.next = TAIL_NEXT;
        }

        @Override
        public RoutineNode getNext() {
            return next;
        }

        // methods for atomic operations
        @Override
        final boolean weakCasNext(RoutineNode c, RoutineNode v) {  // for cleanQueue
            return NEXT.weakCompareAndSet(this, c, v);
        }
        final boolean casStatus(int pre, int now) {
            return STATUS.compareAndSet(this, pre, now);
        }
        final boolean weakCasStatus(int pre, int now) {
            return STATUS.weakCompareAndSet(this, pre, now);
        }
        final void willRemovedAndCancel() {
            while (true) {
                if (status == WAITING) {
                    if (weakCasStatus(WAITING, REMOVING_THEN_CANCEL)) {
                        return;
                    }
                } else if (status == BUSY) {
                    if (weakCasStatus(BUSY, BUSY_TO_REMOVING_THEN_CANCEL)) {
                        return;
                    }
                } else {
                    break;
                }
            }
        }
        final void willRemovedAndResume() {
            while (true) {
                if (status == WAITING) {
                    if (weakCasStatus(WAITING, REMOVING_THEN_RESUME)) {
                        return;
                    }
                } else if (status == BUSY) {
                    if (weakCasStatus(BUSY, BUSY_TO_REMOVING_THEN_RESUME)) {
                        return;
                    }
                } else {
                    break;
                }
            }
        }

        final void destroy() {
            // before call it, we should use cas to nullify the next.
            assert this.next == null;
            this.status = REMOVED;
            this.waiter = null;
            EventHandle handle = this.handle;
            if (handle != null) {
                handle.remove();
            }
        }

        final boolean isInitOrTail() {
            return this.next == TAIL_NEXT;
        }

        public void setHandle(EventHandle handle) {
            if (this.status == REMOVED) {
                handle.remove();
            } else {
                this.handle = handle;
            }
        }
    }

    private static final RoutineNode TAIL_NEXT = new RoutineNode(null, true);

    // Concrete classes tagged by type
    static final class ExclusiveNode extends RoutineNode {
        ExclusiveNode(Waiter waiter, boolean interruptible) {
            super(waiter, interruptible);
        }
    }
    static final class SharedNode extends RoutineNode {
        SharedNode(Waiter waiter, boolean interruptible) {
            super(waiter, interruptible);
        }
    }

    private static boolean isShared(RoutineNode node) {
        return node instanceof SharedNode;
    }

    static final class ConditionNode extends RoutineNode {
        ConditionNode nextWaiter;            // link to next waiting node

        ConditionNode(Waiter waiter, boolean interruptible) {
            super(waiter, interruptible);
        }
    }

    /**
     * Head of the wait queue, lazily initialized.
     */
    private transient final HeadNode head = new HeadNode();

    /**
     * Tail of the wait queue. After initialization, modified only via casTail.
     */
    private transient Node tail = head;

    /**
     * The synchronization state.
     */
    private volatile int state;
    private static final AtomicIntegerFieldUpdater STATE = AtomicIntegerFieldUpdater.newUpdater(AbstractJasyncQueuedSynchronizer.class, "state");

    /**
     * Returns the current value of synchronization state.
     * This operation has memory semantics of a {@code volatile} read.
     * @return current state value
     */
    protected final int getState() {
        return state;
    }

    /**
     * Sets the value of synchronization state.
     * This operation has memory semantics of a {@code volatile} write.
     * @param newState the new state value
     */
    protected final void setState(int newState) {
        state = newState;
    }

    /**
     * Atomically sets synchronization state to the given updated
     * value if the current state value equals the expected value.
     * This operation has memory semantics of a {@code volatile} read
     * and write.
     *
     * @param expect the expected value
     * @param update the new value
     * @return {@code true} if successful. False return indicates that the actual
     *         value was not equal to the expected value.
     */
    protected final boolean compareAndSetState(int expect, int update) {
        return STATE.compareAndSet(this, expect, update);
    }

    /**
     * Atomically sets synchronization state to the given updated
     * value if the current state value equals the expected value.
     * This operation has memory semantics of a {@code volatile} read
     * and write.
     *
     * @param expect the expected value
     * @param update the new value
     * @return {@code true} if successful. False return indicates that the actual
     *         value was not equal to the expected value.
     */
    protected final boolean weakCompareAndSetState(int expect, int update) {
        return STATE.weakCompareAndSet(this, expect, update);
    }

    /**
     * Attempts to acquire in exclusive mode. This method should query
     * if the state of the object permits it to be acquired in the
     * exclusive mode, and if so to acquire it.
     *
     * 

This method is always invoked by the thread performing * acquire. If this method reports failure, the acquire method * may queue the thread, if it is not already queued, until it is * signalled by a release from some other thread. This can be used * to implement method {@link Lock#tryLock()}. * *

The default * implementation throws {@link UnsupportedOperationException}. * * @param arg the acquire argument. This value is always the one * passed to an acquire method, or is the value saved on entry * to a condition wait. The value is otherwise uninterpreted * and can represent anything you like. * @return {@code true} if successful. Upon success, this object has * been acquired. * @throws IllegalMonitorStateException if acquiring would place this * synchronizer in an illegal state. This exception must be * thrown in a consistent fashion for synchronization to work * correctly. * @throws UnsupportedOperationException if exclusive mode is not supported */ protected boolean tryAcquire(JAsyncRoutine current, int arg) { throw new UnsupportedOperationException(); } /** * Attempts to set the state to reflect a release in exclusive * mode. * *

This method is always invoked by the thread performing release. * *

The default implementation throws * {@link UnsupportedOperationException}. * * @param arg the release argument. This value is always the one * passed to a release method, or the current state value upon * entry to a condition wait. The value is otherwise * uninterpreted and can represent anything you like. * @return {@code true} if this object is now in a fully released * state, so that any waiting threads may attempt to acquire; * and {@code false} otherwise. * @throws IllegalMonitorStateException if releasing would place this * synchronizer in an illegal state. This exception must be * thrown in a consistent fashion for synchronization to work * correctly. * @throws UnsupportedOperationException if exclusive mode is not supported */ protected boolean tryRelease(JAsyncRoutine current, int arg) { throw new UnsupportedOperationException(); } /** * Attempts to acquire in shared mode. This method should query if * the state of the object permits it to be acquired in the shared * mode, and if so to acquire it. * *

This method is always invoked by the thread performing * acquire. If this method reports failure, the acquire method * may queue the thread, if it is not already queued, until it is * signalled by a release from some other thread. * *

The default implementation throws {@link * UnsupportedOperationException}. * * @param arg the acquire argument. This value is always the one * passed to an acquire method, or is the value saved on entry * to a condition wait. The value is otherwise uninterpreted * and can represent anything you like. * @return a negative value on failure; zero if acquisition in shared * mode succeeded but no subsequent shared-mode acquire can * succeed; and a positive value if acquisition in shared * mode succeeded and subsequent shared-mode acquires might * also succeed, in which case a subsequent waiting thread * must check availability. (Support for three different * return values enables this method to be used in contexts * where acquires only sometimes act exclusively.) Upon * success, this object has been acquired. * @throws IllegalMonitorStateException if acquiring would place this * synchronizer in an illegal state. This exception must be * thrown in a consistent fashion for synchronization to work * correctly. * @throws UnsupportedOperationException if shared mode is not supported */ protected int tryAcquireShared(JAsyncRoutine current, int arg) { throw new UnsupportedOperationException(); } /** * Attempts to set the state to reflect a release in shared mode. * *

This method is always invoked by the thread performing release. * *

The default implementation throws * {@link UnsupportedOperationException}. * * @param arg the release argument. This value is always the one * passed to a release method, or the current state value upon * entry to a condition wait. The value is otherwise * uninterpreted and can represent anything you like. * @return {@code true} if this release of shared mode may permit a * waiting acquire (shared or exclusive) to succeed; and * {@code false} otherwise * @throws IllegalMonitorStateException if releasing would place this * synchronizer in an illegal state. This exception must be * thrown in a consistent fashion for synchronization to work * correctly. * @throws UnsupportedOperationException if shared mode is not supported */ protected boolean tryReleaseShared(JAsyncRoutine current, int arg) { throw new UnsupportedOperationException(); } /** * Main acquire method, invoked by all exported acquire methods. * * @param node null unless a reacquiring Condition * @param arg the acquire argument * @param shared true if shared mode else exclusive * @param interruptible true if the waiter can be canceled * @param timeout 0 for not timed, positive value for timed in nanosecond. */ @SuppressWarnings("SameParameterValue") final void acquire(Waiter waiter, RoutineNode node, int arg, boolean shared, boolean interruptible, long timeout) { if (node == null) { node = shared ? new SharedNode(waiter, interruptible) : new ExclusiveNode(waiter, interruptible); node.waiter = waiter; } assert node.waiter == waiter; assert node.next == TAIL_NEXT; node.status = WAITING; enqueue(node); if (timeout > 0) { node.setHandle(SCHEDULE.addEvent(timeout, node::willRemovedAndResume)); } if (node == head.next) { signal(arg); } } final void signal(int arg) { RoutineNode node = this.head.next; while (node != TAIL_NEXT) { if (node.status == WAITING) { // if fail try again, so just use weak version. if (node.weakCasStatus(WAITING, BUSY)) { // status = BUSY | BUSY_TO_REMOVING_THEN_CANCEL | BUSY_TO_REMOVING_THEN_RESUME int acquired; try { acquired = isShared(node) ? tryAcquireShared(node.waiter.getRoutine(), arg) : (tryAcquire(node.waiter.getRoutine(), arg) ? 0 : -1); if (acquired >= 0) { node.waiter.resume(true); } } catch (Throwable t) { node.waiter.reject(t); acquired = 1; } // acquired failed if (acquired < 0) { // still BUSY, so change back to WAITING // we must promise cas failure mean others have changed the status, so here we can't use weak version cas. if (node.status == BUSY && node.casStatus(BUSY, WAITING)) { break; } // not BUSY or some others change the status to BUSY_TO_REMOVING_THEN_CANCEL | BUSY_TO_REMOVING_THEN_RESUME assert node.status == BUSY_TO_REMOVING_THEN_CANCEL || node.status == BUSY_TO_REMOVING_THEN_RESUME; // No others have chance to dequeue, so it is always succeed to dequeue here. if (dequeue(node)) { if (node.status == BUSY_TO_REMOVING_THEN_CANCEL) { node.waiter.cancel(); } else if (node.status == BUSY_TO_REMOVING_THEN_RESUME) { node.waiter.resume(false); } else { throw new RuntimeException("This is impossible."); } node.destroy(); // Since the current node is dequeued and processed, we should try the next one. node = this.head.next; } else { throw new RuntimeException("This is impossible."); } } else { // acquired succeed. so we should remove the node. // current status is BUSY | BUSY_TO_REMOVING_THEN_CANCEL | BUSY_TO_REMOVING_THEN_RESUME, so no others have chance to dequeue the node. // The dequeue operation should always successful. if (dequeue(node)) { node.destroy(); // if subsequent shared-mode acquires might also succeed, we should start next loop to signal. if (acquired != 0) { node = this.head.next; } else { break; } } else { throw new RuntimeException("This is impossible."); } } } } else if (node.status == REMOVING_THEN_CANCEL) { // The status REMOVING_THEN_CANCEL can only be change to REMOVED, so we don't need to try again in a loop. // Here using CAS just because of not do the work more than once. if (dequeue(node) && node.casStatus(REMOVING_THEN_CANCEL, REMOVED)) { node.waiter.cancel(); node.destroy(); node = this.head.next; } else { break; } } else if (node.status == REMOVING_THEN_RESUME) { // The status REMOVING_THEN_RESUME can only be change to REMOVED, so we don't need to try again in a loop. // Here using CAS just because of not do the work more than once. if (dequeue(node) && node.casStatus(REMOVING_THEN_RESUME, REMOVED)) { node.waiter.resume(false); node.destroy(); node = this.head.next; } else { break; } } else { break; } } } void enqueue(RoutineNode node) { assert node.next == TAIL_NEXT; while (true) { Node tail = tryFindTail(this.tail); if (tail.getNext() == TAIL_NEXT && tail.weakCasNext(TAIL_NEXT, node)) { this.tail = tryFindTail(node); break; } } } private Node tryFindTail(Node from) { assert from != null; Node node = from; RoutineNode next = from.getNext(); while (next != null && next != TAIL_NEXT) { node = next; next = node.getNext(); } return node; } boolean dequeue(RoutineNode node) { assert node != null && node != TAIL_NEXT; HeadNode head = this.head; RoutineNode n = head.next; while (n == node) { RoutineNode next = n.next; if (next != null && n.weakCasNext(next, null)) { head.next = next; if (n == tail) { tail = tryFindTail(head); } return true; } else { n = head.next; } } return false; } /** * Acquires in exclusive mode, ignoring interrupts. Implemented * by invoking at least once {@link #tryAcquire}, * returning on success. Otherwise the thread is queued, possibly * repeatedly blocking and unblocking, invoking {@link * #tryAcquire} until success. This method can be used * to implement method {@link Lock#lock}. * * @param waiter the waiter. * @param arg the acquire argument. This value is conveyed to * {@link #tryAcquire} but is otherwise uninterpreted and * can represent anything you like. */ public final void acquire(Waiter waiter, int arg) { try { if (!tryAcquire(waiter.getRoutine(), arg)) { acquire(waiter, null, arg, false, false, 0L); } else { waiter.resume(true); } } catch (Throwable t) { waiter.reject(t); } } /** * Acquires in exclusive mode, aborting if interrupted. * Implemented by first checking interrupt status, then invoking * at least once {@link #tryAcquire}, returning on * success. Otherwise the thread is queued, possibly repeatedly * blocking and unblocking, invoking {@link #tryAcquire} * until success or the thread is interrupted. This method can be * used to implement method {@link Lock#lockInterruptibly}. * * @param arg the acquire argument. This value is conveyed to * {@link #tryAcquire} but is otherwise uninterpreted and * can represent anything you like. */ public final void acquireInterruptibly(Waiter waiter, int arg) { try { if (!tryAcquire(waiter.getRoutine(), arg)) { acquire(waiter, null, arg, false, true, 0L); } else { waiter.resume(true); } } catch (Throwable t) { waiter.reject(t); } } /** * Attempts to acquire in exclusive mode, aborting if interrupted, * and failing if the given timeout elapses. Implemented by first * checking interrupt status, then invoking at least once {@link * #tryAcquire}, returning on success. Otherwise, the thread is * queued, possibly repeatedly blocking and unblocking, invoking * {@link #tryAcquire} until success or the thread is interrupted * or the timeout elapses. This method can be used to implement * method {@link Lock#tryLock(long, TimeUnit)}. * * @param waiter the waiter. * @param arg the acquire argument. This value is conveyed to * {@link #tryAcquire} but is otherwise uninterpreted and * can represent anything you like. * @param nanosTimeout the maximum number of nanoseconds to wait */ public final void tryAcquireNanos(Waiter waiter, int arg, long nanosTimeout) { if (tryAcquire(waiter.getRoutine(), arg)) { waiter.resume(true); return; } if (nanosTimeout <= 0L) { waiter.resume(false); return; } acquire(waiter, null, arg, false, true, nanosTimeout); } /** * Releases in exclusive mode. Implemented by unblocking one or * more threads if {@link #tryRelease} returns true. * This method can be used to implement method {@link Lock#unlock}. * * @param arg the release argument. This value is conveyed to * {@link #tryRelease} but is otherwise uninterpreted and * can represent anything you like. * @return the value returned from {@link #tryRelease} */ public final boolean release(JAsyncRoutine current, int arg) { if (tryRelease(current, arg)) { signal(arg); return true; } return false; } /** * Acquires in shared mode, ignoring interrupts. Implemented by * first invoking at least once {@link #tryAcquireShared}, * returning on success. Otherwise the thread is queued, possibly * repeatedly blocking and unblocking, invoking {@link * #tryAcquireShared} until success. * * @param waiter the waiter. * @param arg the acquire argument. This value is conveyed to * {@link #tryAcquireShared} but is otherwise uninterpreted * and can represent anything you like. */ public final void acquireShared(Waiter waiter, int arg) { try { if (tryAcquireShared(waiter.getRoutine(), arg) < 0) { acquire(waiter, null, arg, true, false, 0L); } else { waiter.resume(true); } } catch (Throwable t) { waiter.reject(t); } } /** * Acquires in shared mode, aborting if interrupted. Implemented * by first checking interrupt status, then invoking at least once * {@link #tryAcquireShared}, returning on success. Otherwise the * thread is queued, possibly repeatedly blocking and unblocking, * invoking {@link #tryAcquireShared} until success or the thread * is interrupted. * @param arg the acquire argument. * This value is conveyed to {@link #tryAcquireShared} but is * otherwise uninterpreted and can represent anything * you like. */ public final void acquireSharedInterruptibly(Waiter waiter, int arg) { try { if (tryAcquireShared(waiter.getRoutine(), arg) < 0) { acquire(waiter, null, arg, true, true, 0L); } else { waiter.resume(true); } } catch (Throwable t) { waiter.reject(t); } } /** * Attempts to acquire in shared mode, aborting if interrupted, and * failing if the given timeout elapses. Implemented by first * checking interrupt status, then invoking at least once {@link * #tryAcquireShared}, returning on success. Otherwise, the * thread is queued, possibly repeatedly blocking and unblocking, * invoking {@link #tryAcquireShared} until success or the thread * is interrupted or the timeout elapses. * * @param waiter the waiter. * @param arg the acquire argument. This value is conveyed to * {@link #tryAcquireShared} but is otherwise uninterpreted * and can represent anything you like. * @param nanosTimeout the maximum number of nanoseconds to wait */ public final void tryAcquireSharedNanos(Waiter waiter, int arg, long nanosTimeout) { if (tryAcquireShared(waiter.getRoutine(), arg) >= 0) { waiter.resume(true); return; } if (nanosTimeout <= 0L) { waiter.resume(false); return; } acquire(waiter, null, arg, true, true, System.nanoTime() + nanosTimeout); } /** * Releases in shared mode. Implemented by unblocking one or more * threads if {@link #tryReleaseShared} returns true. * * @param arg the release argument. This value is conveyed to * {@link #tryReleaseShared} but is otherwise uninterpreted * and can represent anything you like. * @return the value returned from {@link #tryReleaseShared} */ public final boolean releaseShared(JAsyncRoutine current, int arg) { if (tryReleaseShared(current, arg)) { signal(arg); return true; } return false; } /** * Interpret the waiter. * @param waiter the waiter. * @return ture if interpret successfully, false if the waiter is not in the queue. * it means the waiter has been resumed or not valid. */ public final boolean interpret(Waiter waiter) { RoutineNode node = head.getNext(); while (node != TAIL_NEXT) { if (node != null) { if (node.waiter == waiter) { node.willRemovedAndCancel(); // There should be only one node found. Because the node in the queue means its waiter is waiting // Only when the waiter resumed, the waiter can be enqueued again. return true; } node = node.getNext(); } } return false; } /** * The current owner of exclusive mode synchronization. */ private transient long exclusiveRoutineId; /** * Sets the thread that currently owns exclusive access. * A {@code null} argument indicates that no thread owns access. * This method does not otherwise impose any synchronization or * {@code volatile} field accesses. * @param routine the owner routine */ protected final void setExclusiveRoutine(JAsyncRoutine routine) { exclusiveRoutineId = routine != null ? routine.id() : 0L; } /** * Returns {@code true} if synchronization is held exclusively with * respect to the current (calling) thread. This method is invoked * upon each call to a {@link AbstractQueuedSynchronizer.ConditionObject} method. * *

The default implementation throws {@link * UnsupportedOperationException}. This method is invoked * internally only within {@link AbstractQueuedSynchronizer.ConditionObject} methods, so need * not be defined if conditions are not used. * * @return {@code true} if synchronization is held exclusively; * {@code false} otherwise * @throws UnsupportedOperationException if conditions are not supported */ protected boolean isNotHeldExclusively(JAsyncRoutine current) { return current.id() != exclusiveRoutineId; } /** * Returns {@code true} if the apparent first queued waiter, if one * exists, is waiting in exclusive mode. If this method returns * {@code true}, and the current waiter is attempting to acquire in * shared mode (that is, this method is invoked from {@link * #tryAcquireShared}) then it is guaranteed that the current waiter * is not the first queued waiter. Used only as a heuristic in * ReentrantReadWriteLock. */ final boolean apparentlyFirstQueuedIsExclusive() { RoutineNode s; return (s = this.head.next) != TAIL_NEXT && s != null && !isShared(s) && s.status == WAITING; } /** * Queries whether any waiters have been waiting to acquire longer * than the current waiter. * *

An invocation of this method is equivalent to (but may be * more efficient than): *

 {@code
     * getFirstQueuedThread() != Thread.currentThread()
     *   && hasQueuedThreads()}
* *

Note that because cancellations due to interrupts and * timeouts may occur at any time, a {@code true} return does not * guarantee that some other waiter will acquire before the current * waiter. Likewise, it is possible for another waiter to win a * race to enqueue after this method has returned {@code false}, * due to the queue being empty. * *

This method is designed to be used by a fair synchronizer to * avoid barging. * Such a synchronizer's {@link #tryAcquire} method should return * {@code false}, and its {@link #tryAcquireShared} method should * return a negative value, if this method returns {@code true} * (unless this is a reentrant acquire). For example, the {@code * tryAcquire} method for a fair, reentrant, exclusive mode * synchronizer might look like this: * *

 {@code
     * protected boolean tryAcquire(int arg) {
     *   if (isHeldExclusively()) {
     *     // A reentrant acquire; increment hold count
     *     return true;
     *   } else if (hasQueuedPredecessors()) {
     *     return false;
     *   } else {
     *     // try to acquire normally
     *   }
     * }}
* * @return {@code true} if there is a queued waiter preceding the * current waiter, and {@code false} if the current thread * is at the head of the queue or the queue is empty * @since 1.7 */ public final boolean hasQueuedPredecessors(JAsyncRoutine current) { Waiter first; RoutineNode s; return (s = this.head.next) != TAIL_NEXT && s != null && s.status > 0 && s.waiter.getRoutine().id() != current.id(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy