package nachos.threads;
import nachos.machine.*;
import java.util.*;
/**
* Uses the hardware timer to provide preemption, and to allow threads to sleep
* until a certain time.
*/
public class Alarm {
/**
* Allocate a new Alarm. Set the machine's timer interrupt handler to this
* <p><b>Note</b>: Nachos will not function correctly with more than one
* alarm.
*/
public Alarm() {
Lib.debug(dbgAlarm, "Creating Alarm" + Machine.timer().getTime());
timeCreated = Machine.timer().getTime();
waitQueue = new ArrayList<KThread>();
timeQueue = new ArrayList<Long>();
queueLock = new Lock();
Machine.timer().setInterruptHandler(new Runnable() {
public void run() { timerInterrupt(); }
});
}
/**
* The timer interrupt handler. This is called by the machine's timer
* periodically (approximately every 500 clock ticks). Causes the current
* thread to yield, forcing a context switch if there is another thread
* that should be run.
*/
public void timerInterrupt() {
Lib.debug(dbgAlarm,"In Interrupt Handler (time = "+Machine.timer().getTime()+")");
//Disable interrupts
boolean intStatus = Machine.interrupt().disable();
KThread thread;
//If there is a task that is waiting, restore it to ready status
for (int i = 0; i < waitQueue.size(); i++) {
if (Machine.timer().getTime() > timeQueue.get(i)) {
waitQueue.get(i).ready();
waitQueue.remove(i);
timeQueue.remove(i);
}
}
//Restore interrupts
Machine.interrupt().restore(intStatus);
//Current thread yields and context switches
KThread.yield();
}
/**
* Put the current thread to sleep for at least <i>x</i> ticks,
* waking it up in the timer interrupt handler. The thread must be
* woken up (placed in the scheduler ready set) during the first timer
* interrupt where
*
* <p><blockquote>
* (current time) >= (WaitUntil called time)+(x)
* </blockquote>
*
* @param x the minimum number of clock ticks to wait.
*
* @see nachos.machine.Timer#getTime()
*/
public void waitUntil(long x) {
//Sets current thread as waitThread
//Initializes wakeTime with x ticks
long wakeTime = Machine.timer().getTime() + x;
//Disable interrupts
boolean intStatus = Machine.interrupt().disable();
queueLock.acquire();
//Puts task to sleep for x ticks
waitQueue.add(KThread.currentThread());
Lib.debug(dbgAlarm, "Added new task size="+ waitQueue.size() + " timeCreated="+this.timeCreated);
timeQueue.add(wakeTime);
int i;
queueLock.release();
KThread.sleep();
//Restores interrupts
Machine.interrupt().restore(intStatus);
}
/**
* Tests whether this module is working.
*/
public static void selfTest() {
AlarmTest.runTest();
}
private static final char dbgAlarm = 'a';
private List<KThread> waitQueue;
private List<Long> timeQueue;
private Lock queueLock;
private long timeCreated;
}