Using the @command{gdb} debugger, slowly trace through a context
switch to see what happens (@pxref{i386-elf-gdb}). You can set a
breakpoint on the @func{schedule} function to start out, and then
-single-step from there. Be sure to keep track of each thread's
-address and state, and what procedures are on the call stack for each
-thread. You will notice that when one thread calls
-@func{switch_threads}, another thread starts running, and the first
-thing the new thread does is to return from
-@func{switch_threads}. We realize this comment will seem cryptic to
-you at this point, but you will understand threads once you understand
-why the @func{switch_threads} that gets called is different from the
-@func{switch_threads} that returns.
+single-step from there.@footnote{@command{gdb} might tell you that
+@func{schedule} doesn't exist, which is arguably a @command{gdb} bug.
+You can work around this by setting the breakpoint by filename and
+line number, e.g.@: @code{break thread.c:@var{ln}} where @var{ln} is
+the line number of the first declaration in @func{schedule}.
+Alternatively you can recompile with optimization turned off, by
+removing @samp{-O3} from the @code{CFLAGS} line in
+@file{Make.config}.} Be sure to keep track of each thread's address
+and state, and what procedures are on the call stack for each thread.
+You will notice that when one thread calls @func{switch_threads},
+another thread starts running, and the first thing the new thread does
+is to return from @func{switch_threads}. We realize this comment will
+seem cryptic to you at this point, but you will understand threads
+once you understand why the @func{switch_threads} that gets called is
+different from the @func{switch_threads} that returns.
@strong{Warning}: In Pintos, each thread is assigned a small,
fixed-size execution stack just under @w{4 kB} in size. The kernel
than the currently running thread, the current thread should
immediately yield the processor to the new thread. Similarly, when
threads are waiting for a lock, semaphore or condition variable, the
-highest priority waiting thread should be woken up first. A thread's
-priority may be set at any time, including while the thread is waiting
-on a lock, semaphore, or condition variable.
+highest priority waiting thread should be woken up first. A thread
+may set its priority at any time.
One issue with priority scheduling is ``priority inversion'': if a
high priority thread needs to wait for a low priority thread (for
Don't worry about the possibility of timer values overflowing. Timer
values are expressed as signed 63-bit numbers, which at 100 ticks per
second should be good for almost 2,924,712,087 years.
+
+@item
+@b{The test program mostly works but reports a few out-of-order
+wake ups. I think it's a problem in the test program. What gives?}
+
+This test is inherently full of race conditions. On a real system it
+wouldn't work perfectly all the time either. However, you can help it
+work more reliably:
+
+@itemize @bullet
+@item
+Make time slices longer by increasing @code{TIME_SLICE} in
+@file{timer.c} to a large value, such as 100.
+
+@item
+Make the timer tick more slowly by decreasing @code{TIMER_FREQ} in
+@file{timer.h} to its minimum value of 19.
+
+@item
+Increase the serial output speed to the maximum of 115,200 bps by
+modifying the call to @func{set_serial} in @func{serial_init_poll} in
+@file{devices/serial.c}.
+@end itemize
+
+The former two changes are only desirable for testing problem 1-1. You
+should revert them before working on other parts of the project or turn
+in the project. The latter is harmless, so you can retain it or revert
+it at your option.
@end enumerate
@node Problem 1-2 Join FAQ