/*
 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
 *
 *  This file is licensed to You under the Common Public License (CPL);
 *  You may not use this file except in compliance with the License. You
 *  may obtain a copy of the License at
 *
 *      http://www.opensource.org/licenses/cpl1.0.php
 *
 *  See the COPYRIGHT.txt file distributed with this work for information
 *  regarding copyright ownership.
 */
package org.jikesrvm.scheduler.greenthreads;

import org.jikesrvm.VM;
import org.jikesrvm.runtime.Time;
import org.vmmagic.pragma.Uninterruptible;

/**
 * A queue of Proxys prioritized by their thread wakeup times.
 * based on WakeupQueue (14 October 1998 Bowen Alpern)
 */
@Uninterruptible
public final class ThreadProxyWakeupQueue extends AbstractThreadQueue {

  /** first thread on list */
  private ThreadProxy head;

  /**
   * Are any proxies on the queue?
   */
  @Override
  public boolean isEmpty() {
    return head == null;
  }

  /**
   * Is the head of the queue (the thread set to wake up first) ready to be
   * restarted?
   */
  public boolean isReady() {
    ThreadProxy temp = head;
    return ((temp != null) && (Time.nanoTime() >= temp.getWakeupNano()));
  }

  /**
   * Put proxy for this thread on the queue.
   * Since a processor lock is held, the proxy cannot be created here.
   * Instead, it is cached in the proxy field of the thread.
   */
  @Override
  public void enqueue(GreenThread t) {
    enqueue(t.threadProxy);
  }

  /**
   * Add the proxy for a thread in a place determined by its wakeup time
   */
  public void enqueue(ThreadProxy p) {
    if (p != null) {
      p.mutex.lock("Enqueueing proxy");
      if (p.getPatron() != null) {
        if(VM.VerifyAssertions) VM._assert(p.getPatron().isQueueable());
        ThreadProxy previous = null;
        ThreadProxy current = head;
        // skip proxies with earlier wakeup timestamps
        while (current != null && current.getWakeupNano() <= p.getWakeupNano()) {
          previous = current;
          current = current.getWakeupNext();
        }
        // insert p
        if (previous == null) {
          head = p;
        } else {
          previous.setWakeupNext(p);
        }
        p.setWakeupNext(current);
      }
      p.mutex.unlock();
    }
  }

  /**
   * Remove a thread from the queue if there's one ready to wake up "now".
   * Returned: the thread (null --> nobody ready to wake up)
   */
  @Override
  public GreenThread dequeue() {
    long currentNano = Time.nanoTime();
    while (head != null) {
      if (currentNano < head.getWakeupNano()) return null;
      ThreadProxy p = head;
      head = head.getWakeupNext();
      p.setWakeupNext(null);
      GreenThread t = p.unproxy();
      if (t != null) return t;
    }
    return null;
  }

  /**
   * Number of items on queue (an estimate: queue is not locked during the
   * scan).
   */
  @Override
  public int length() {
    if (head == null) return 0;
    int length = 1;
    for (ThreadProxy p = head; p != null; p = p.getWakeupNext()) {
      length += 1;
    }
    return length;
  }

  // Debugging.
  //
  public boolean contains(GreenThread t) {
    for (ThreadProxy p = head; p != null; p = p.getWakeupNext()) {
      if (p.getPatron() == t) return true;
    }
    return false;
  }

  public void dump() {
    if (head != null) {
      VM.sysWrite(" nowNano=", Time.nanoTime());
      VM.sysWrite(" ");
      for (ThreadProxy p = head; p != null; p = p.getWakeupNext()) {
        if (p.getPatron() != null) {
          p.getPatron().dump();
          VM.sysWrite("(wakeupNano=", p.getWakeupNano()); VM.sysWrite(") ");
        }
      }
    }
    VM.sysWrite("\n");
  }
}
