forked from NachoSquad/nachos
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Interrupt.java
253 lines (205 loc) · 6.69 KB
/
Interrupt.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
// 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);
}
}
}