Phase 1 code hints

advertisement
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.
Download