-@node Debugging versus Testing
-@section Debugging versus Testing
-
-When you're debugging code, it's useful to be able to be able to run a
-program twice and have it do exactly the same thing. On second and
-later runs, you can make new observations without having to discard or
-verify your old observations. This property is called
-``reproducibility.'' The simulator we use, Bochs, can be set up for
-reproducibility. If you use the Bochs configuration files we provide,
-which specify @samp{ips: @var{n}} where @var{n} is a number of
-simulated instructions per second, your simulations can be
-reproducible.
-
-Of course, a simulation can only be reproducible from one run to the
-next if its input is the same each time. For simulating an entire
-computer, as we do, this means that every part of the computer must be
-the same. For example, you must use the same disks, the same version
-of Bochs, and you must not hit any keys on the keyboard (because you
-could not be sure to hit them at exactly the same point each time)
-during the runs.
-
-While reproducibility is useful for debugging, it is a problem for
-testing thread synchronization, an important part of this project. In
-particular, when Bochs is set up for reproducibility, timer interrupts
-will come at perfectly reproducible points, and therefore so will
-thread switches. That means that running the same test several times
-doesn't give you any greater confidence in your code's correctness
-than does running it only once.
-
-So, to make your code easier to test, we've added a feature to Bochs
-that makes timer interrupts come at random intervals, but in a
-perfectly predictable way. In particular, if you put a line
-@samp{ips-jitter: @var{seed}}, where @var{seed} is an integer, into
-your Bochs configuration file, then timer interrupts will come at
-irregularly spaced intervals. Within a single @var{seed} value,
-execution will still be reproducible, but timer behavior will change
-as @var{seed} is varied. Thus, for the highest degree of confidence
-you should test your code with many seed values.
-
-@node Tips
-@section Tips
-
-There should be no busy-waiting in any of your solutions to this
-assignment. Furthermore, resist the temptation to directly disable
-interrupts in your solution by calling @code{intr_disable()} or
-@code{intr_set_level()}, although you may find doing so to be useful
-while debugging. Instead, use semaphores, locks and condition
-variables to solve synchronization problems. Hint: read the comments
-in @file{threads/synch.h} if you're unsure what synchronization
-primitives may be used in what situations.
-
-Given some designs of some problems, there may be one or two instances
-in which it is appropriate to directly change the interrupt levels
-instead of relying on the given synchroniztion primitives. This must
-be justified in your @file{DESIGNDOC} file. If you're not sure you're
-justified, ask!
-
-While all parts of this assignment are required if you intend to earn
-full credit on this project, keep in mind that Problem 2 (Join) will
-be needed for future assignments, so you'll want to get this one
-right. We don't give out solutions, so you're stuck with your Join
-code for the whole quarter. Problem 1 (Alarm Clock) could be very
-handy, but not strictly required in the future. The upshot of all
-this is that you should focus heavily on making sure that your
-implementation of Join works correctly, since if it's broken, you will
-need to fix it for future assignments. The other parts can be turned
-off in the future if you find you can't make them work quite right.
-
-Also keep in mind that Problem 4 (the MLFQS) builds on the features you
-implement in Problem 3, so to avoid unnecessary code duplication, it
-would be a good idea to divide up the work among your team members
-such that you have Problem 3 fully working before you begin to tackle
-Problem 4.
-
-@node Problem 1-1 Alarm Clock
-@section Problem 1-1: Alarm Clock
-
-Improve the implementation of the timer device defined in
-@file{devices/timer.c} by reimplementing @code{timer_sleep()}.
-Threads call @code{timer_sleep(@var{x})} to suspend execution until
-time has advanced by at least @w{@var{x} timer ticks}. This is
-useful for threads that operate in real-time, for example, for
-blinking the cursor once per second. There is no requirement that
-threads start running immediately after waking up; just put them on
-the ready queue after they have waited for approximately the right
-amount of time.
-
-A working implementation of this function is provided. However, the
-version provided is poor, because it ``busy waits,'' that is, it spins
-in a tight loop checking the current time until the current time has
-advanced far enough. This is undesirable because it wastes time that
-could potentially be used more profitably by another thread. Your
-solution should not busy wait.
-
-The argument to @code{timer_sleep()} is expressed in timer ticks, not
-in milliseconds or some other unit.
-
-@node Problem 1-2 Join
-@section Problem 1-2: Join
-
-Implement @code{thread_join(struct thread *)} in
-@file{threads/thread.c}. There is already a prototype for it in
-@file{threads/thread.h}, which you should not change. This function
-causes the currently running thread to block until thread passed as an
-argument exits. If A is the running thread and B is the argument,
-then we say that ``A joins B'' in this case.
-
-The model for @code{thread_join()} is the @command{wait} system call
-in Unix-like systems. (Try reading the manpages.) That system call
-can only be used by a parent process to wait for a child's death. You
-should implement @code{thread_join()} to have the same restriction.
-That is, a thread may only join on its immediate children.
-
-A thread need not ever be joined. Your solution should properly free
-all of a thread's resources, including its @code{struct thread},
-whether it is ever joined or not, and regardless of whether the child
-exits before or after its parent. That is, a thread should be freed
-exactly once in all cases.
-
-Joining a given thread is idempotent. That is, joining a thread T
-multiple times is equivalent to joining it once, because T has already
-exited at the time of the later joins. Thus, joins on T after the
-first should return immediately.
-
-The behavior of calling @code{thread_join()} on an thread that is not
-the caller's child is undefined. You need not handle this case
-gracefully.
-
-Consider all the ways a join can occur: nested joins (A joins B when B
-is joined on C), multiple joins (A joins B, then A joins C), and so
-on. Does your join work if @code{thread_join()} is called on a thread
-that has not yet been scheduled for the first time? You should handle
-all of these cases. Write test code that demonstrates the cases your
-join works for. Don't overdo the output volume, please!
-
-Be careful to program this function correctly. You will need its
-functionality for project 2.
-
-@node Problem 1-3 Priority Scheduling
-@section Problem 1-3 Priority Scheduling
-
-Implement priority scheduling in Pintos. Priority
-scheduling is a key building block for real-time systems. Implement functions
-@code{thread_set_priority()} to set the priority of a thread and
-@code{thread_get_priority()} to get the priority of a thread. There
-are already prototypes for these functions in @file{threads/thread.h},
-which you should not change.