/*
* 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.VM_Time;
import org.jikesrvm.scheduler.VM_Thread;
import org.vmmagic.pragma.Interruptible;
import org.vmmagic.pragma.Uninterruptible;
/**
* Queue of threads waiting for a specific kind of event to occur.
* This class contains the high level functionality of enqueueing
* and dequeueing threads and implementing timeouts.
* Subclasses implement methods which determine when events
* have occurred. Subclasses <em>must</em> directly implement the
* {@link Uninterruptible} interface.
*
* <p>This class was adapted from the original
* <code>VM_ThreadIOQueue</code>, which is now a subclass.
*
*
* @see VM_ThreadIOQueue
* @see VM_ThreadProcessWaitQueue
* @see VM_ThreadEventConstants
*/
@Uninterruptible
abstract class VM_ThreadEventWaitQueue extends VM_AbstractThreadQueue implements VM_ThreadEventConstants {
protected VM_GreenThread head, tail;
/** Number of queued threads. */
private int length;
/**
* Number of threads ready to run because their events occurred,or their
* timeout expired.
*/
private int ready;
/**
* Is queue empty?
*/
@Override
public boolean isEmpty() {
return length == 0;
}
/**
* Number of threads on queue.
*/
@Override
public int length() {
return length;
}
/**
* Check to see if any threads are ready to run, either because
* their events occurred or their waits timed out.
*/
boolean isReady() {
if (length == 0) {
return false; // no threads waiting
}
if (VM.VerifyAssertions) VM._assert(ready >= 0);
if (ready == 0) {
// No threads are ready, so try to find some that are...
// Allow subclass to check for events
if (!pollForEvents()) {
return false; // possibly transient error; try again later
}
VM_GreenThread thread = head;
long currentNano = VM_Time.nanoTime();
// See if any threads have become ready to run
while (thread != null) {
VM_ThreadEventWaitData waitData = thread.waitData;
long maxWaitNano = waitData.getMaxWaitNano();
if (maxWaitNano > 0 && maxWaitNano < currentNano) {
// Wait timed out
waitData.setFinishedAndTimeout();
++ready;
} else if (isReady(thread)) {
// Subclass has decided that the thread is ready to schedule;
// it should have updated waitFlags appropriately
if (VM.VerifyAssertions) {
VM._assert(waitData.isFinished());
}
++ready;
} else {
waitData.clearFinished();
}
thread = thread.getNext();
}
}
return ready != 0;
}
/**
* Check to see if any events occurred.
* Called prior to calling {@link #isReady(VM_Thread)} on
* queued threads.
* @return whether or not polling was successful
*/
abstract boolean pollForEvents();
/**
* Check to see if the event the given thread is waiting for
* has occurred, or if it should be woken up for any other reason
* (such as being interrupted).
*/
abstract boolean isReady(VM_GreenThread thread);
/**
* Place a thread on this queue.
* Its {@link VM_Thread#waitData waitData} field should
* be set to indicate the event that the thread is waiting for.
* @param thread the thread to put on the queue
*/
@Override
public void enqueue(VM_GreenThread thread) {
if (VM.VerifyAssertions) {
VM._assert(thread.waitData.isPending() || thread.waitData.isNative());
VM._assert(thread.getNext() == null);
}
// Add to queue
if (head == null) {
head = thread;
} else {
tail.setNext(thread);
}
tail = thread;
++length;
}
/**
* Get a thread that has become ready to run.
* @return the thread, or null if no threads from this queue are ready
*/
@Override
public VM_GreenThread dequeue() {
VM_GreenThread prev = null;
VM_GreenThread thread = head;
if (VM.VerifyAssertions) VM._assert(ready >= 0);
// See if a thread is finished waiting
while (thread != null) {
if (thread.waitData.isFinished()) {
break;
}
prev = thread;
thread = thread.getNext();
}
// If we found one, take it off the queue
if (thread != null) {
if (prev == null) {
head = thread.getNext();
} else {
prev.setNext(thread.getNext());
}
if (tail == thread) {
tail = prev;
}
thread.setNext(null);
--length;
--ready;
} else /* thread == null */ {
if (VM.VerifyAssertions) VM._assert(ready == 0);
}
return thread;
}
/*
* Debugging.
*/
/** Does the queue contain the given thread */
final boolean contains(VM_Thread x) {
for (VM_GreenThread t = head; t != null; t = t.getNext()) {
if (t == x) return true;
}
return false;
}
/**
* Dump state for debugging.
*/
@Interruptible
void dump() {
dump(" ");
}
/**
* Dump state for debugging.
*/
@Interruptible
void dump(String prefix) {
VM.sysWrite(prefix);
for (VM_GreenThread t = head; t != null; t = t.getNext()) {
VM.sysWrite(t.getIndex());
dumpWaitDescription(t);
}
VM.sysWrite("\n");
}
/**
* Dump description of what given thread is waiting for.
* For debugging.
*/
@Interruptible
abstract void dumpWaitDescription(VM_GreenThread thread);
/**
* Get string describing what given thread is waiting for.
* This method must be interruptible!
*/
@Interruptible
abstract String getWaitDescription(VM_GreenThread thread);
}