import java.util.*; public class SystemTimer { /** * Number of virtual time units allocated to the processing of a system * call. */ public static final int SYSCALL_TIME = 2; public static final boolean debug = false; private Kernel osKernel; private Vector processes; private EventQueue events; private Hashtable deviceTimes; private long kernelTime; private long systemTime; private long idleTime; private long userTime; private Event currentTimerEvent; private Process idleProcess; /** * Construct a new SystemTimer object. * * @param processes the Vector of processes that this timer will be * simulating. * @param events the EventQueue that holds the events scheduled in the * system. */ public SystemTimer(Vector processes, EventQueue events) { this.processes = processes; this.events = events; deviceTimes = new Hashtable(); idleProcess = new Process("IDLE", 0, null); kernelTime = 0; userTime = 0; systemTime = 0; idleTime = 0; } /** * Set the Kernel object to which this SystemDriver will issue its * interrupts and system calls. * * @param kernel the Kernel object. */ public void setKernel(Kernel kernel) { osKernel = kernel; } public void processEvents() { // while not done // handle any events scheduled for at or before the current time. // (Events are start process or I/O interrupt) // advance the system time as appropriate // // increment the system time // credit 1 time unit to the running process // // if process cpu burst is done // issue the process' request for I/O or termination // advance the system time as appropriate while (!simulationFinished()) { Event nextEvent = events.getNextEvent(); while (nextEvent != null && nextEvent.getTime() <= systemTime) { processEvent(events.removeNextEvent()); nextEvent = events.getNextEvent(); } String runningProcName = osKernel.running(this); Process runningProc = getProcess(runningProcName); if (debug) { /* * Note: The system time reported by this debugging statement * is the time at the start of a time slice. So for example: * ... * 23 running Process1 * 24 running Process2 * ... * means that Process1 runs from time 23 to time 24 and * that Process2 begins running at time 24 and runs through * at least time 25. */ System.out.println(systemTime + "\t\trunning " + runningProc.getName()); } creditProcess(runningProc); } osKernel.terminate(this); } /* * Handle the crediting of a CPU time unit to a process. If it is the idle * process just up the idle time. If it is another process credit it with 1 * unit of CPU time and then check to see if the current CPU burst has * completed. If it has then generate a system call for the process. The * system call will either be an I/O request or a termination request. */ private void creditProcess(Process proc) { if (proc.getName().equalsIgnoreCase("IDLE")) { idleTime++; systemTime++; } else { ProcessOperation pOp = proc.getOp(); int theOp = pOp.getOpType(); if (theOp == ProcessOperation.CPU) { proc.execute(1); userTime++; systemTime++; if (proc.getCPUBurstRemaining() == 0) { generateSystemCall(proc); } } else { throw new RuntimeException("Process (" + proc.getName() + ") reported as running by kernel is " + "not runnable!. It is currently waiting " + "or has already exited."); } } } /* * The process generated a system call. Figure out what type of system call * it was and issue the call to the kernel. If the system call is an I/O * request then this method will put an event into the queue for the * interrupt that is issued when the I/O is complete. If it is one of the * other types of interrupts then the system call is just passed along to * the kernel. */ private void generateSystemCall(Process proc) { ProcessOperation pOp = proc.getOp(); int theOp = pOp.getOpType(); if (theOp == ProcessOperation.IO) { if (debug) { System.out.println(systemTime + "\t\thandling IO System Call (" + pOp.getOpDevice() + ")"); } // Look up the device for the operation in the hash table // if it exists compute the new free time for the device // and put an entry for the device back into the // hash table. Long freeAt = (Long) deviceTimes.remove(pOp.getOpDevice()); long freeTime; if (freeAt != null) { freeTime = freeAt.longValue(); // If the device is not currently free add the time // of the new op to the free time of the device. if (freeTime > systemTime + SYSCALL_TIME) { freeTime = freeTime + pOp.getOpTime(); } else if (freeTime > systemTime) { freeTime = freeTime + pOp.getOpTime() + (systemTime + SYSCALL_TIME - freeTime); } // If the device is currently free it will be free // again after this operation is complete. else { freeTime = systemTime + pOp.getOpTime() + SYSCALL_TIME; } } else { // There was not yet an entry for this device so // it will be free again after this operation is // complete. freeTime = systemTime + pOp.getOpTime() + SYSCALL_TIME; } deviceTimes.put(pOp.getOpDevice(), new Long(freeTime)); events.addEvent(new Event(freeTime, pOp)); osKernel.systemCall(Kernel.IO_REQUEST, pOp.getOpDevice(), this); kernelTime = kernelTime + SYSCALL_TIME; systemTime = systemTime + SYSCALL_TIME; } // else if (theOp == ProcessOperation.WAIT) { // osKernel.systemCall(Kernel.WAIT, pOp.getOpDevice()); // } // else if (theOp == ProcessOperation.NOTIFY) { // osKernel.systemCall(Kernel.NOTIFY, pOp.getOpDevice()); // } // else if (theOp == ProcessOperation.NOTIFY_ALL) { // osKernel.systemCall(Kernel.NOTIFY_ALL, pOp.getOpDevice()); // } else if (theOp == ProcessOperation.EXIT) { if (debug) { System.out.println(systemTime + "\t\thandling EXIT System Call"); } osKernel.systemCall(Kernel.TERMINATE_PROCESS, null, this); kernelTime = kernelTime + SYSCALL_TIME; systemTime = systemTime + SYSCALL_TIME; } else { throw new RuntimeException("SystemTimer.generateSystemCall() " + "should not be handling: " + pOp); } } /* * Process an event scheduled for the current time or earlier. Events can be * a new process starting or an interrupt genereated by a hardware device. */ private void processEvent(Event e) { if (e.getEvent() instanceof String) { if (debug) { System.out.println(systemTime + "\t\tprocessing interrupt from (" + e.getEvent() + ")"); } osKernel.interrupt((String) e.getEvent(), this); kernelTime = kernelTime + SYSCALL_TIME; systemTime = systemTime + SYSCALL_TIME; } else if (e.getEvent() instanceof ProcessOperation) { ProcessOperation pOp = (ProcessOperation) (e.getEvent()); int theOp = pOp.getOpType(); if (theOp == ProcessOperation.START) { if (debug) { System.out.println(systemTime + "\t\tStarting Process (" + pOp.getParent().getName() + ")"); } osKernel.systemCall(Kernel.START_PROCESS, pOp.getParent() .getName(), this); kernelTime = kernelTime + SYSCALL_TIME; systemTime = systemTime + SYSCALL_TIME; } else if (theOp == ProcessOperation.IO) { if (debug) { System.out.println(systemTime + "\t\tprocessing interrupt from (" + pOp.getOpDevice() + ")"); } osKernel.interrupt(pOp.getOpDevice(), this); kernelTime = kernelTime + SYSCALL_TIME; systemTime = systemTime + SYSCALL_TIME; pOp.getParent().advanceOpCounter(); } else { throw new RuntimeException("Event " + pOp + " should not " + "have been found in the event queue."); } } else { throw new RuntimeException("Unrecognized Event " + e + " has been found in the event queue."); } } /* * Get the Process object corresponding to the process name. */ private Process getProcess(String procName) { // Make a phony Process object for the purposes of // searching for the original Process object corresponding // to the name. Process proc = new Process(procName, 0, null); int procIndex = processes.indexOf(proc); // Make sure the kernel reported a valid process... if (procIndex != -1) { // Get the real Process object and process it. proc = (Process) processes.get(procIndex); return proc; } else if (procName.equalsIgnoreCase("IDLE")) { return idleProcess; } else { throw new RuntimeException("Process (" + procName + ") reported as running by " + "kernel does not exist."); } } /* * Determine if the simulation has concluded. */ private boolean simulationFinished() { // Put the termination condition here. // probably that all of the processes have reached their termination // Also need a way to bail if the simulation isn't making progress. // E.g. for some reason the scheduler isn't running the ready processes. boolean allDone = true; Iterator pit = processes.iterator(); while (pit.hasNext()) { Process p = (Process) pit.next(); allDone = allDone && p.finished(); } return allDone; } /** * Schedule a timer interrupt to occur after the specified number of time * units. The timer is set to begin when the system call that started it * completes. Thus a process started to run when a timer is set will be * allowed to run for a full "delay" time units. * Only one timer interrupt can be scheduled at a time. If the event * queue already contains a timer interrupt that timer interrupt will be * canceled and a new one created. * * @param delay the number of time units before the timer interrupt will * occur. */ public void scheduleTimerInterrupt(int delay) { cancelTimerInterrupt(); currentTimerEvent = new Event(systemTime + delay + SYSCALL_TIME, "TIMER"); events.addEvent(currentTimerEvent); } /** * Get the amount of time remaining before the currently set timer interrupt * occurs. If no timer interrupt is currently pending, then the value -1 will * be returned. * * @return the number of time units before the currently pending timer interrupt * will occur, or -1 if no timer interrupt is pending. */ public int getTimerTicksRemaining() { if (currentTimerEvent != null) { long timerTime = currentTimerEvent.getTime(); if (timerTime >= systemTime) { return (int)(timerTime - systemTime); } else { return -1; } } else { return -1; } } /** * Cancel a timer interrupt if one has been set. If a timer interrupt has * been set, it is removed from the event queue. If no timer interrupt has * been set this method does nothing. */ public void cancelTimerInterrupt() { // Remove any timer interrupt that appears in the event queue. if (currentTimerEvent != null) { events.remove(currentTimerEvent); currentTimerEvent = null; } } /** * Return the current kernel time. This is the number of virtual time units * that the system has spent performing kernel operations. * * @return the current kernel time. */ public long getKernelTime() { return kernelTime; } /** * Return the current user time. This is the number of virtual time units * that the system has spent performing user operations. * * @return the current user time. */ public long getUserTime() { return userTime; } /** * Return the current idle time. This is the number of virtual time units * that the system has spent executing the idle process. * * @return the current idle time. */ public long getIdleTime() { return idleTime; } /** * Return the current system time. This is the total number of virtual time * units that have elapsed since the system has started. It should agree * with the value obtained from getKernelTime() + getUserTime() + * getIdleTime(); * * @return the current system time. */ public long getSystemTime() { return systemTime; } }