diff --git a/README.md b/README.md index 0660c39..65ae91d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # Multi-Level Feedback Queue + ### Motivation After talking about one of the most popular scheduling algorithms used by operating systems to schedule processes, let's implement it! This will be an implementation that simulates an actual multi-level feedback queue processing diff --git a/src/Process.js b/src/Process.js index 159a307..001e951 100644 --- a/src/Process.js +++ b/src/Process.js @@ -16,11 +16,11 @@ class Process { } setParentQueue(queue) { - + this.queue = queue; } isFinished() { - + return this.cpuTimeNeeded === 0 && this.blockingTimeNeeded === 0; } // If no blocking time is needed by this process, decrement the amount of @@ -29,7 +29,15 @@ class Process { // by emitting the appropriate interrupt // Make sure the `stateChanged` flag is toggled appropriately executeProcess(time) { - + this.stateChanged = false; + if (this.blockingTimeNeeded === 0) { + this.cpuTimeNeeded -= time; + // if we get a negative value, its 0 + this.cpuTimeNeeded = this.cpuTimeNeeded > 0 ? this.cpuTimeNeeded : 0; + } else { + this.queue.emitInterrupt(this, SchedulerInterrupt.PROCESS_BLOCKED); + this.stateChanged = true; + } } // If this process requires blocking time, decrement the amount of blocking @@ -38,16 +46,22 @@ class Process { // top running queue by emitting the appropriate interrupt // Make sure the `stateChanged` flag is toggled appropriately executeBlockingProcess(time) { + this.blockingTimeNeeded -= time; + this.blockingTimeNeeded = this.blockingTimeNeeded > 0 ? this.blockingTimeNeeded : 0; + if (this.blockingTimeNeeded === 0) { + this.queue.emitInterrupt(this, SchedulerInterrupt.PROCESS_READY); + this.stateChanged = true; + } } // Returns this process's stateChanged property isStateChanged() { - + return this.stateChanged; } get pid() { - + return this._pid; } // Private function used for testing; DO NOT MODIFY diff --git a/src/Queue.js b/src/Queue.js index 7b45fdf..fa6a1e0 100644 --- a/src/Queue.js +++ b/src/Queue.js @@ -19,29 +19,31 @@ class Queue { // Enqueues the given process. Return the enqueue'd process enqueue(process) { - + process.setParentQueue(this); + this.processes.push(process); + return process; } // Dequeues the next process in the queue. Return the dequeue'd process dequeue() { - + return this.processes.shift(); } // Return the least-recently added process without removing it from the list of processes peek() { - + return this.processes[0]; } isEmpty() { - + return this.processes.length === 0; } getPriorityLevel() { - + return this.priorityLevel; } getQueueType() { - + return this.queueType; } // Manages a process's execution for the given amount of time @@ -49,26 +51,49 @@ class Queue { // Once a process has received the alloted time, it needs to be dequeue'd and // then handled accordingly, depending on whether it has finished executing or not manageTimeSlice(currentProcess, time) { + if (currentProcess.isStateChanged()) { + this.quantumClock = 0; + return; + } + + this.quantumClock += time; + + if (this.quantumClock >= this.quantum) { + this.quantumClock = 0; + const process = this.dequeue(); + + if (!process.isFinished()) { + this.scheduler.handleInterrupt(this, process, SchedulerInterrupt) + } + } } // Execute the next non-blocking process (assuming this is a CPU queue) // This method should call `manageTimeSlice` as well as execute the next running process doCPUWork(time) { - + const process = this.peek(); + process.executeProcess(time); + this.manageTimeSlice(process, time); } // Execute the next blocking process (assuming this is the blocking queue) // This method should call `manageTimeSlice` as well as execute the next blocking process doBlockingWork(time) { - + const process = this.peek(); + process.executeBlockingProcess(time); + this.manageTimeSlice(process, time); } // The queue's interrupt handler for notifying when a process needs to be moved to a different queue // Should handle PROCESS_BLOCKED and PROCESS_READY interrupts // The process also needs to be removed from the queue emitInterrupt(source, interrupt) { - + const sourceIndex = this.processes.indexOf(source); + // console.log("LOOK HERE:", sourceIndex); + // console.log(this.processes) + this.processes.splice(sourceIndex, 1); + this.scheduler.handleInterrupt(this, source, interrupt); } } diff --git a/src/Scheduler.js b/src/Scheduler.js index 3bca870..afa0aa2 100644 --- a/src/Scheduler.js +++ b/src/Scheduler.js @@ -1,6 +1,7 @@ const Queue = require('./Queue'); const { QueueType, + SchedulerInterrupt, PRIORITY_LEVELS, } = require('./constants/index'); @@ -24,21 +25,59 @@ class Scheduler { // On every iteration of the scheduler, if the blocking queue is not empty, blocking work // should be done. Once the blocking work has been done, perform some CPU work in the same iteration. run() { - + while (true) { + const time = Date.now(); + const timeSlice = time - this.clock; + this.clock = time; + + if (!this.blockingQueue.isEmpty()) { + this.blockingQueue.doBlockingWork(timeSlice); + } + + for (let i=0; i queue.isEmpty()) && this.blockingQueue.isEmpty(); } addNewProcess(process) { - + this.runningQueues[0].enqueue(process); } // The scheduler's interrupt handler that receives a queue, a process, and an interrupt string constant // Should handle PROCESS_BLOCKED, PROCESS_READY, and LOWER_PRIORITY interrupts. handleInterrupt(queue, process, interrupt) { - + switch(interrupt) { + case SchedulerInterrupt.PROCESS_BLOCKED: + this.blockingQueue.enqueue(process); + break; + case SchedulerInterrupt.PROCESS_READY: + this.addNewProcess(process); + break; + case SchedulerInterrupt.LOWER_PRIORITY: + if (queue.getQueueType() === QueueType.CPU_QUEUE) { + const priorityLevel = Math.min(PRIORITY_LEVELS-1, queue.getPriorityLevel()+1); + this.runningQueues[priorityLevel].enqueue(process); + } else { + this.blockingQueue.enqueue(process); + } + break; + default: + break; + } } // Private function used for testing; DO NOT MODIFY