A Simple Scheduler, with I/O

This is part 2 of the scheduler assignment. You will modify your solution to the previous assignment to support I/O operations by the Jobs. As before, the end result should be a Gannt chart based on the actual running times of the threads.

The Details:

By completing the previous assignment you should already have most of the major parts completed for this assignment, so there shouldn't be all that much code you have to write. Instead, you're going to have to modify your existing code a bit. Notably, you'll have to make more use of your Timer objects and interrupts.

Input File:

The input file will be named scheduleInput.txt, as before, but is a bit more complicated in this assignment. Each line of input is now of the form:

jobID delayTillSubmission CPUburst [IOburst CPUburst]*

Again, the first argument will be an integer, and this must somehow be used in the name of the Job (how is up to you). The second argument is the number of milliseconds that should elapse from the submission of the Job corresponding to the previous input line until this Job is submitted to the SystemSimulator via AddNewProcess. (If this is the first line of the file, then delayTillSubmissionshould be measured from the start time of the SystemSimulator itself.

The syntax of the above statement is borrowed from the help pages in Unix: the square brackets indicate optional arguments. The asterix is the Kleene Star, such as is found in regular expressions, representing 0 or more repetitions. In other words, the input lines are the same as in the previous assignment but with the possibility of any number of pairs of IOburst and CPU burst lengths at the end of each line, specified as integers. Here is a sample input file. (As before, the times are all given in milliseconds.) Your Job threads should run until their current CPU burst is complete (again, not counting time spent on the readyQ), and at that point begin an IO burst, during which time another Job may be able to run. Upon completing the IO burst, the Job should be placed on the readyQ. (You may need to interrupt the sleeping scheduler if there are no Jobs currently running.)

Here is the output that might be generated by running on that input.

Adding Simulated Input

Add a method to the SystemSimulator class called doIO(int msec). This will act as a system call that your Job class should invoke when it is time for a simulated IO operation. The argument will specify the duration of the IO operation.

Your solution should allow for another Job to run while the IO operation completes. As with the first project, your solution should allow for only one of either the kernel or any Job thread to be runing at a time. You shouldn't have two Jobs running (i.e., invoking their JobWorkable's doWork() method) at the same time. At most one Job should be running, with the remainder (if any) either on the ready queue, or on a data structure of your choice for storing the Jobs that are awaiting their IO completion (you might use a ConcurrentLinkedQueue for this).

When a Job's IO completes it should be placed back onto the ready queue. You'll need to alter the makeRun(), because now the ready queue will contain Jobs that have never started, as well as those that were previously started, but did an IO and were then placed back on the ready queue. You might want to use Thread.isAlive() to differentiate the two types.

Your solution should use a simple Thread class, IODevice to simulate the IO device. These will act like a simpler version of the Submittor class. They will sleep for the duration of the IO, and when they awaken, they will place their particular Job thread back onto the ready queue. As with the submittor thread, its okay for them to be running simultaneosly with other threads.

Properly coding SystemSimulator.doIO is tricky. As IO is starting you have to deal with three threads simultaneously: the kernel, a Job that is starting IO, and the IODevice thread that will simulate that IO. Keep in mind that as soon as the Job sends a signal() to its Condition the kernel (which was previously blocked via an await() on the same Condition) can immediately start running. Beware of the possible race condition: you don't want the kernel to terminate prematurely. The Job thread needs to create its IODevice and start it, and then block itself on its Condition. There are other solutions than using ReentrantLocks/Conditions, too. For example, which might use sleep() and interrupt(), or you might use wait() and notify().

Testing Your Code

You should submit all of your Java files as usual. As with the previous project I will provide my own WorkFactory class that provides a zero argument createWork() method.

I will supply an input file, named scheduleInput.txt, sconforming to the syntax described above. I will run your program and examine the resulting Gannt chart.

To Hand In:

Submit your code to the drop box, along with a text file containing the console output of your program running the example input file.

Hints:

See my suggested path to victory!

If you decide to use sleep() in combination with interrupt(), you might want to check out this short tutorial on interrupts.

As I mentioned in the previous project, you may want to use Java's StreamTokenizer class to parse the lines from the input file. Alternatively, you can use String.split(" ") to break each input line into constituent substrings.

This link is to a document explaining the potential for null reference exceptions resulting from invoking methods on "dead" threads.