/*
  based on file: QueuedSemaphore.java
  Originally written by Doug Lea and released into the public domain.
  This may be used for any purposes whatsoever without acknowledgment.
  Thanks for the assistance and support of Sun Microsystems Labs,
  and everyone contributing, testing, and using this code.
  History:
  Date       Who                What
  11Jun1998  dl               Create public version
   5Aug1998  dl               replaced int counters with longs
  24Aug1999  dl               release(n): screen arguments
 */

package edu.emory.mathcs.backport.java.util.concurrent.helpers;

import java.util.Collection;
import edu.emory.mathcs.backport.java.util.concurrent.*;

/**
 * Base class for internal queue classes for semaphores, etc.
 * Relies on subclasses to actually implement queue mechanics.
 * NOTE: this class is NOT present in java.util.concurrent.
 **/

public abstract class WaitQueue {

    public abstract void insert(WaitNode w); // assumed not to block
    public abstract WaitNode extract(); // should return null if empty
    public abstract void putBack(WaitNode w);

    public abstract boolean hasNodes();
    public abstract int getLength();
    public abstract Collection getWaitingThreads();
    public abstract boolean isWaiting(Thread thread);

    public static interface QueuedSync {
        // invoked with sync on wait node, (atomically) just before enqueuing
        boolean recheck(WaitNode node);
        // invoked with sync on wait node, (atomically) just before signalling
        void takeOver(WaitNode node);
    }

    public static class WaitNode {
        boolean waiting = true;
        WaitNode next = null;
        final Thread owner;

        public WaitNode() {
            this.owner = Thread.currentThread();
        }

        public Thread getOwner() {
            return owner;
        }

        public synchronized boolean signal(QueuedSync sync) {
            boolean signalled = waiting;
            if (signalled) {
                waiting = false;
                notify();
                sync.takeOver(this);
            }
            return signalled;
        }

        public synchronized boolean doTimedWait(QueuedSync sync, long nanos)
            throws InterruptedException
        {
            if (sync.recheck(this) || !waiting)
                return true;
            else if (nanos <= 0) {
                waiting = false;
                return false;
            }
            else {
                long deadline = Utils.nanoTime() + nanos;
                try {
                    for (; ; ) {
                        TimeUnit.NANOSECONDS.timedWait(this, nanos);
                        if (!waiting) // definitely signalled
                            return true;
                        else {
                            nanos = deadline - Utils.nanoTime();
                            if (nanos <= 0) { //  timed out
                                waiting = false;
                                return false;
                            }
                        }
                    }
                }
                catch (InterruptedException ex) {
                    if (waiting) { // no notification
                        waiting = false; // invalidate for the signaller
                        throw ex;
                    }
                    else { // thread was interrupted after it was notified
                        Thread.currentThread().interrupt();
                        return true;
                    }
                }
            }
        }

        public synchronized void doWait(QueuedSync sync)
            throws InterruptedException
        {
            if (!sync.recheck(this)) {
                try {
                    while (waiting) wait();
                }
                catch (InterruptedException ex) {
                    if (waiting) { // no notification
                        waiting = false; // invalidate for the signaller
                        throw ex;
                    }
                    else { // thread was interrupted after it was notified
                        Thread.currentThread().interrupt();
                        return;
                    }
                }
            }
        }

        public synchronized void doWaitUninterruptibly(QueuedSync sync) {
            if (!sync.recheck(this)) {
                boolean wasInterrupted = Thread.interrupted();
                try {
                    while (waiting) {
                        try {
                            wait();
                        }
                        catch (InterruptedException ex) {
                            wasInterrupted = true;
                            // no need to notify; if we were signalled, we
                            // must be not waiting, and we'll act like signalled
                        }
                    }
                }
                finally {
                    if (wasInterrupted) Thread.currentThread().interrupt();
                }
            }
        }
    }
}

