p1/machine/Interrupt.java

254 lines
6.7 KiB
Java

// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
import nachos.security.*;
import java.util.TreeSet;
import java.util.Iterator;
import java.util.SortedSet;
/**
* The <tt>Interrupt</tt> class emulates low-level interrupt hardware. The
* hardware provides a method (<tt>setStatus()</tt>) to enable or disable
* interrupts.
*
* <p>
* In order to emulate the hardware, we need to keep track of all pending
* interrupts the hardware devices would cause, and when they are supposed to
* occur.
*
* <p>
* This module also keeps track of simulated time. Time advances only when the
* following occur:
* <ul>
* <li>interrupts are enabled, when they were previously disabled
* <li>a MIPS instruction is executed
* </ul>
*
* <p>
* As a result, unlike real hardware, interrupts (including time-slice context
* switches) cannot occur just anywhere in the code where interrupts are
* enabled, but rather only at those places in the code where simulated time
* advances (so that it becomes time for the hardware simulation to invoke an
* interrupt handler).
*
* <p>
* This means that incorrectly synchronized code may work fine on this hardware
* simulation (even with randomized time slices), but it wouldn't work on real
* hardware. But even though Nachos can't always detect when your program
* would fail in real life, you should still write properly synchronized code.
*/
public final class Interrupt {
/**
* Allocate a new interrupt controller.
*
* @param privilege encapsulates privileged access to the Nachos
* machine.
*/
public Interrupt(Privilege privilege) {
System.out.print(" interrupt");
this.privilege = privilege;
privilege.interrupt = new InterruptPrivilege();
enabled = false;
pending = new TreeSet<PendingInterrupt>();
}
/**
* Enable interrupts. This method has the same effect as
* <tt>setStatus(true)</tt>.
*/
public void enable() {
setStatus(true);
}
/**
* Disable interrupts and return the old interrupt state. This method has
* the same effect as <tt>setStatus(false)</tt>.
*
* @return <tt>true</tt> if interrupts were enabled.
*/
public boolean disable() {
return setStatus(false);
}
/**
* Restore interrupts to the specified status. This method has the same
* effect as <tt>setStatus(<i>status</i>)</tt>.
*
* @param status <tt>true</tt> to enable interrupts.
*/
public void restore(boolean status) {
setStatus(status);
}
/**
* Set the interrupt status to be enabled (<tt>true</tt>) or disabled
* (<tt>false</tt>) and return the previous status. If the interrupt
* status changes from disabled to enabled, the simulated time is advanced.
*
* @param status <tt>true</tt> to enable interrupts.
* @return <tt>true</tt> if interrupts were enabled.
*/
public boolean setStatus(boolean status) {
boolean oldStatus = enabled;
enabled = status;
if (oldStatus == false && status == true)
tick(true);
return oldStatus;
}
/**
* Tests whether interrupts are enabled.
*
* @return <tt>true</tt> if interrupts are enabled.
*/
public boolean enabled() {
return enabled;
}
/**
* Tests whether interrupts are disabled.
*
* @return <tt>true</tt> if interrupts are disabled.
*/
public boolean disabled() {
return !enabled;
}
private void schedule(long when, String type, Runnable handler) {
Lib.assertTrue(when>0);
long time = privilege.stats.totalTicks + when;
PendingInterrupt toOccur = new PendingInterrupt(time, type, handler);
Lib.debug(dbgInt,
"Scheduling the " + type +
" interrupt handler at time = " + time);
pending.add(toOccur);
}
private void tick(boolean inKernelMode) {
Stats stats = privilege.stats;
if (inKernelMode) {
stats.kernelTicks += Stats.KernelTick;
stats.totalTicks += Stats.KernelTick;
}
else {
stats.userTicks += Stats.UserTick;
stats.totalTicks += Stats.UserTick;
}
if (Lib.test(dbgInt))
System.out.println("== Tick " + stats.totalTicks + " ==");
enabled = false;
checkIfDue();
enabled = true;
}
private void checkIfDue() {
long time = privilege.stats.totalTicks;
Lib.assertTrue(disabled());
if (Lib.test(dbgInt))
print();
if (pending.isEmpty())
return;
if (((PendingInterrupt) pending.first()).time > time)
return;
Lib.debug(dbgInt, "Invoking interrupt handlers at time = " + time);
while (!pending.isEmpty() &&
((PendingInterrupt) pending.first()).time <= time) {
PendingInterrupt next = (PendingInterrupt) pending.first();
pending.remove(next);
Lib.assertTrue(next.time <= time);
if (privilege.processor != null)
privilege.processor.flushPipe();
Lib.debug(dbgInt, " " + next.type);
next.handler.run();
}
Lib.debug(dbgInt, " (end of list)");
}
private void print() {
System.out.println("Time: " + privilege.stats.totalTicks
+ ", interrupts " + (enabled ? "on" : "off"));
System.out.println("Pending interrupts:");
for (Iterator i=pending.iterator(); i.hasNext(); ) {
PendingInterrupt toOccur = (PendingInterrupt) i.next();
System.out.println(" " + toOccur.type +
", scheduled at " + toOccur.time);
}
System.out.println(" (end of list)");
}
private class PendingInterrupt implements Comparable {
PendingInterrupt(long time, String type, Runnable handler) {
this.time = time;
this.type = type;
this.handler = handler;
this.id = numPendingInterruptsCreated++;
}
public int compareTo(Object o) {
PendingInterrupt toOccur = (PendingInterrupt) o;
// can't return 0 for unequal objects, so check all fields
if (time < toOccur.time)
return -1;
else if (time > toOccur.time)
return 1;
else if (id < toOccur.id)
return -1;
else if (id > toOccur.id)
return 1;
else
return 0;
}
long time;
String type;
Runnable handler;
private long id;
}
private long numPendingInterruptsCreated = 0;
private Privilege privilege;
private boolean enabled;
private TreeSet<PendingInterrupt> pending;
private static final char dbgInt = 'i';
private class InterruptPrivilege implements Privilege.InterruptPrivilege {
public void schedule(long when, String type, Runnable handler) {
Interrupt.this.schedule(when, type, handler);
}
public void tick(boolean inKernelMode) {
Interrupt.this.tick(inKernelMode);
}
}
}