Nachos Phase 1 Code -Hints and Comments Task I –Implement join() Join method: Should have a test to prevent a thread from trying to join itself. Interrupts should be disabled – they must be disabled before calling sleep(). The calling thread is the current thread and it must be stored so that it can be awoken later. I think it’s a good idea to store the threads in a list so that more than one thread can join a given thread. The documentation seems to suggest that a thread can only be joined once, so I didn’t take marks off for not handling multiple threads, although this seems an unnecessary limitation. You may find having multiple threads join a thread useful in testing. Task I (continued) Don’t forget to wake the sleeping thread(s). This should happen in the finish method. The sleeping threads should be moved back to the ready queue by calling the ready() method. Some groups put the code to wake the thread in the restoreState method just before the thread was destroyed. This should work as well, since the finished thread should be destroyed by the next thread. In general though, the time a thread finishes and the time it is destroyed may not be the same, so I think it is better to put the code in the finish method. Task II Implement Condition2 This task was generally well done. Make sure to check that the current thread has the lock for all methods. The sleep method stores the threads waiting on this condition in a queue. After waking the thread must re-acquire the lock. Interrupts should be disabled to make the operation atomic. The wake methods removes the first element in the queue (unless the queue is empty) and puts it back on the ready queue. Again, interrupts should be disabled to make the operation atomic. The wakeAll method is similar, but instead of waking just one thread it wakes all the threads in the queue. Task III Implement waitUntil method The waitUntil(long x) method must first determine the wake time of the thread by adding x to the current time. The waiting threads must be stored in a queue. The most efficient method is to use a priority queue so the whole queue doesn’t have to be searched. Remember, the timerInterrupt method is called every 500 ticks, so it should be efficient. The problem is that we have to store both the thread and the wake time. One way to do this is to create a new class, consisting of the thread and the wake time, that implements the Comparable interface. Then implement the compareTo method to allow objects of this type to be sorted by the wake time. (You should have learned how to do this in the Data Structures course). Task III (continued) Java now has a priority queue (as of version 1.5) but nachos doesn’t use java 1.5 so you won’t be able to use it. The priority queue can also be easily implemented with a linked list – just insert the objects in ascending order. Other groups used heaps or Treesets to implement the priority queue. These methods should work as well. If a priority queue is used, the timerInterrupt can use a while loop to wake the threads whose wake time is less than the current time. Task IV Implement Communicator This is the most difficult of the tasks. One key to getting this class to work is to understand how different variable types work in threads. In a context switch the variables on the stack are saved for each thread. These include the parameters and local variables. This means that parameters and local variables are unique for each thread. In contrast, data on the heap is global to all threads. This includes any instance variables. Since in this class we must pass data from the speaker to the listener we need to use an instance variable to do this. Task IV (Continued) The speak(int word) method: One detail to determine, is whether the speaker should transfer the message (i.e. move the word from the parameter to the instance variable) before or after going to sleep on the condition. If we move the message before going to sleep we have the problem of it being overwritten by another speaker before it is picked up by a listener. Remember that the sleep method in the condition class releases the lock. This means a number of speakers can be waiting on the condition at the same time, so we must ensure that their messages don’t get lost. The way our group got around this was to store the messages in a queue. However, Doctor Passi doesn’t like this method, so I will suggest another method. Task IV (Continued) If the speaker can’t store the message before going to sleep, it must do it after waking. This requires a compound condition; the speaker must sleep while either there are no listeners or the message from the previous speaker has not yet been picked up by a listener (this will require a boolean instance variable). Once the speaker exits the while loop it can leave its message and set the boolean variable to indicate that there is a message to pick up because it knows there is at least one listener waiting. It then wakes a listener and exits. Task IV (Continued) The int listen() method: After entering the critical region, the listener increments the counter keeping track of the number of listeners waiting. It then wakes a speaker (if there is one). This ensures that if a speaker was only waiting for a listener (no message waiting from the last speaker) it will be woken. The listener must sleep while there is no message to pick up (the boolean variable). On exiting the while loop it decrements the number of listeners waiting and can pick up the message. The message should be moved to a local variable. This is because the listener must release the lock before returning. This means the instance variable containing the message might be overwritten before the listener returns the message. After moving the message to a local variable, the listener sets the boolean variable to indicate the message has been picked up (no message waiting). Then wake up a speaker (if there is one), since there may be a speaker waiting only for the boolean flag (there are other listeners). The listener can then release the lock and return the local variable. Task IV (Continued) You should just use one lock, but it’s probably simpler to use 2 condition variables, one for the speakers and one for the listeners, if you use this method. Task V Implement ReactWater This task was well done by all groups, so I have no additional comments to make. Additional Comments Test Cases Its important to think about how to test your code. The test cases should include both what you want to test and an outline of how you are going to test it. You should test both normal conditions and any special cases. Compiling The Code The group0X/nachos (X is your group number) directory contains the file Makefile. This file contains the names of the source files to be compiled listed by directory. To get it to compile ReactWater type ReactWater in the threads section. E.g. threads = ThreadedKernel KThread Alarm \ Scheduler ThreadQueue RoundRobinScheduler \ Semaphore Lock Condition SynchList \ Condition2 Communicator Rider ElevatorController \ PriorityScheduler LotteryScheduler Boat ReactWater Add any other new classes in here as well. To compile, go to directory group0X/nachos/proj1 and type gmake. To run nachos key in nachos in the same directory (group0X/nachos/proj1). Where to put Your Test Code When nachos starts up it calls ThreadedKernel (in the threads directory). ThreadedKernel has a method called selfTest. You can add your testing code here. Probably the cleanest way to do this is to have selfTest methods in each of the classes and call these methods. –i.e. in ThreadedKernel’s selfTest add: Condition2.selfTest(); ReactWater.selfTest(); etc. Add the test code to the selfTest method in each class. Testing When the main thread ends, nachos will shut down. This isn’t supposed to happen. According to the documentation it should wait for all threads to finish. This easiest way to get around this problem is to have the main thread join each test thread, so the main thread doesn’t end until all test threads have finished. This won’t work if your join is limited to one thread, which is one reason I recommend keeping a queue of joined threads in task I.