Update docs.
[pintos-anon] / doc / threads.texi
1 @node Project 1--Threads, Project 2--User Programs, Introduction, Top
2 @chapter Project 1: Threads
3
4 In this assignment, we give you a minimally functional thread system.
5 Your job is to extend the functionality of this system to gain a
6 better understanding of synchronization problems. Additionally, you
7 will use at least part of this increased functionality in future
8 assignments.
9
10 You will be working in primarily in the @file{threads} directory for
11 this assignment, with some work in the @file{devices} directory on the
12 side.  Compilation should be done in the @file{threads} directory.
13
14 @menu
15 * Understanding Threads::       
16 * Project 1 Code::              
17 * Debugging versus Testing::    
18 * Tips::                        
19 * Problem 1-1 Alarm Clock::     
20 * Problem 1-2 Join::            
21 * Problem 1-3 Priority Scheduling::  
22 * Problem 1-4 Advanced Scheduler::  
23 * Threads FAQ::                 
24 @end menu
25
26 @node Understanding Threads
27 @section Understanding Threads
28
29 The first step is to read and understand the initial thread system.
30 Pintos, by default, implements thread creation and thread completion,
31 a simple scheduler to switch between threads, and synchronization
32 primitives (semaphores, locks, and condition variables). 
33
34 However, there's a lot of magic going on in some of this code, so if
35 you haven't already compiled and run the base system, as described in
36 the introduction (@pxref{Introduction}), you should do so now.  You
37 can read through parts of the source code by hand to see what's going
38 on.  If you like, you can add calls to @code{printf()} almost
39 anywhere, then recompile and run to see what happens and in what
40 order.  You can also run the kernel in a debugger and set breakpoints
41 at interesting spots, single-step through code and examine data, and
42 so on.  @xref{i386-elf-gdb}, for more information.
43
44 When a thread is created, you are creating a new context to be
45 scheduled. You provide a function to be run in this context as an
46 argument to @code{thread_create()}. The first time the thread is
47 scheduled and runs, it will start from the beginning of that function
48 and execute it in the context. When that function returns, that thread
49 completes. Each thread, therefore, acts like a mini-program running
50 inside Pintos, with the function passed to @code{thread_create()}
51 acting like @code{main()}.
52
53 At any given time, Pintos is running exactly one thread, with the
54 others switched out.  The scheduler decides which thread to run next
55 when it needs to switch between them.  (If no thread is ready to run
56 at any given time, then the special ``idle'' thread runs.)  The
57 synchronization primitives are used to force context switches when one
58 thread needs to wait for another thread to do something.
59
60 The exact mechanics of a context switch are pretty gruesome and have
61 been provided for you in @file{threads/switch.S} (this is 80@var{x}86
62 assembly; don't worry about understanding it).  It involves saving the
63 state of the currently running thread and restoring the state of the
64 thread we're switching to.
65
66 Using the @command{gdb} debugger, slowly trace through a context
67 switch to see what happens (@pxref{i386-elf-gdb}).  You can set a
68 breakpoint on the @code{schedule()} function to start out, and then
69 single-step from there.  Be sure to keep track of each thread's
70 address and state, and what procedures are on the call stack for each
71 thread.  You will notice that when one thread calls
72 @code{switch_threads()}, another thread starts running, and the first
73 thing the new thread does is to return from
74 @code{switch_threads()}.  We realize this comment will seem cryptic to
75 you at this point, but you will understand threads once you understand
76 why the @code{switch_threads()} that gets called is different from the
77 @code{switch_threads()} that returns.  @c FIXME
78
79 @strong{Warning}: In Pintos, each thread is assigned a small,
80 fixed-size execution stack just under @w{4 kB} in size.  The kernel
81 does try to detect stack overflow, but it cannot always succeed.  You
82 ma cause bizarre problems, such as mysterious kernel panics, if you
83 declare large data structures as non-static local variables,
84 e.g. @samp{int buf[1000];}.  Alternatives to stack allocation include
85 the page allocator in @file{threads/palloc.c} and the block allocator
86 in @file{threads/malloc.c}.  Note that the page allocator doles out
87 @w{4 kB} chunks and that @code{malloc()} has a @w{2 kB} block size
88 limit.  If you need larger chunks, consider using a linked structure
89 instead.
90
91 @node Project 1 Code
92 @section Code
93
94 Here is a brief overview of the files in the @file{threads}
95 directory.  You will not need to modify most of this code, but the
96 hope is that presenting this overview will give you a start on what
97 code to look at.
98
99 @table @file
100 @item loader.S
101 @itemx loader.h
102 The kernel loader.  Assembles to 512 bytes of code and data that the
103 PC BIOS loads into memory and which in turn loads the kernel into
104 memory, does basic processor initialization, and jumps to the
105 beginning of the kernel.  You should not need to look at this code or
106 modify it.
107
108 @item kernel.lds.S
109 The linker script used to link the kernel.  Sets the load address of
110 the kernel and arranges for @file{start.S} to be at the very beginning
111 of the kernel image.  Again, you should not need to look at this code
112 or modify it, but it's here in case you're curious.
113
114 @item start.S
115 Jumps to @code{main()}.
116
117 @item init.c
118 @itemx init.h
119 Kernel initialization, including @code{main()}, the kernel's ``main
120 program.''  You should look over @code{main()} at least to see what
121 gets initialized.
122
123 @item thread.c
124 @itemx thread.h
125 Basic thread support.  Much of your work will take place in these
126 files.  @file{thread.h} defines @code{struct thread}, which you will
127 modify in the first three projects.
128
129 @item switch.S
130 @itemx switch.h
131 Assembly language routine for switching threads.  Already discussed
132 above.
133
134 @item palloc.c
135 @itemx palloc.h
136 Page allocator, which hands out system memory one 4 kB page at a time.
137
138 @item malloc.c
139 @itemx malloc.h
140 A very simple implementation of @code{malloc()} and @code{free()} for
141 the kernel.  The largest block that can be allocated is 2 kB.
142
143 @item interrupt.c
144 @itemx interrupt.h
145 Basic interrupt handling and functions for turning interrupts on and
146 off.
147
148 @item intr-stubs.pl
149 @itemx intr-stubs.h
150 A Perl program that outputs assembly for low-level interrupt handling.
151
152 @item synch.c
153 @itemx synch.h
154 Basic synchronization primitives: semaphores, locks, and condition
155 variables.  You will need to use these for synchronization through all
156 four projects.
157
158 @item test.c
159 @itemx test.h
160 Test code.  For project 1, you will replace this file with your test
161 cases.
162
163 @item io.h
164 Functions for I/O port access.  This is mostly used by source code in
165 the @file{devices} directory that you won't have to touch.
166
167 @item mmu.h
168 Functions and macros related to memory management, including page
169 directories and page tables.  This will be more important to you in
170 project 3.  For now, you can ignore it.
171 @end table
172
173 FIXME devices and lib directories?
174
175 @node Debugging versus Testing
176 @section Debugging versus Testing
177
178 When you're debugging code, it's useful to be able to be able to run a
179 program twice and have it do exactly the same thing.  On second and
180 later runs, you can make new observations without having to discard or
181 verify your old observations.  This property is called
182 ``reproducibility.''  The simulator we use, Bochs, can be set up for
183 reproducibility.  If you use the Bochs configuration files we provide,
184 which specify @samp{ips: @var{n}} where @var{n} is a number of
185 simulated instructions per second, your simulations can be
186 reproducible.
187
188 Of course, a simulation can only be reproducible from one run to the
189 next if its input is the same each time.  For simulating an entire
190 computer, as we do, this means that every part of the computer must be
191 the same.  For example, you must use the same disks, the same version
192 of Bochs, and you must not hit any keys on the keyboard (because you
193 could not be sure to hit them at exactly the same point each time)
194 during the runs.
195
196 While reproducibility is useful for debugging, it is a problem for
197 testing thread synchronization, an important part of this project.  In
198 particular, when Bochs is set up for reproducibility, timer interrupts
199 will come at perfectly reproducible points, and therefore so will
200 thread switches.  That means that running the same test several times
201 doesn't give you any greater confidence in your code's correctness
202 than does running it only once.
203
204 So, to make your code easier to test, we've added a feature to Bochs
205 that makes timer interrupts come at random intervals, but in a
206 perfectly predictable way.  In particular, if you invoke
207 @command{pintos} with the option @option{-j @var{seed}}, timer
208 interrupts will come at irregularly spaced intervals.  Within a single
209 @var{seed} value, execution will still be reproducible, but timer
210 behavior will change as @var{seed} is varied.  Thus, for the highest
211 degree of confidence you should test your code with many seed values.
212
213 @node Tips
214 @section Tips
215
216 There should be no busy-waiting in any of your solutions to this
217 assignment.  Furthermore, resist the temptation to directly disable
218 interrupts in your solution by calling @code{intr_disable()} or
219 @code{intr_set_level()}, although you may find doing so to be useful
220 while debugging.  Instead, use semaphores, locks and condition
221 variables to solve synchronization problems.  Hint: read the comments
222 in @file{threads/synch.h} if you're unsure what synchronization
223 primitives may be used in what situations.
224
225 Given some designs of some problems, there may be one or two instances
226 in which it is appropriate to directly change the interrupt levels
227 instead of relying on the given synchroniztion primitives.  This must
228 be justified in your @file{DESIGNDOC} file.  If you're not sure you're
229 justified, ask!
230
231 While all parts of this assignment are required if you intend to earn
232 full credit on this project, keep in mind that Problem 1-2 (Join) will
233 be needed for future assignments, so you'll want to get this one
234 right.  We don't give out solutions, so you're stuck with your Join
235 code for the whole quarter.  Problem 1-1 (Alarm Clock) could be very
236 handy, but not strictly required in the future.  The upshot of all
237 this is that you should focus heavily on making sure that your
238 implementation of @code{thread_join()} works correctly, since if it's
239 broken, you will need to fix it for future assignments.  The other
240 parts can be turned off in the future if you find you can't make them
241 work quite right.
242
243 Also keep in mind that Problem 1-4 (the MLFQS) builds on the features you
244 implement in Problem 1-3, so to avoid unnecessary code duplication, it
245 would be a good idea to divide up the work among your team members
246 such that you have Problem 1-3 fully working before you begin to tackle
247 Problem 1-4.
248
249 @node Problem 1-1 Alarm Clock
250 @section Problem 1-1: Alarm Clock
251
252 Improve the implementation of the timer device defined in
253 @file{devices/timer.c} by reimplementing @code{timer_sleep()}.
254 Threads call @code{timer_sleep(@var{x})} to suspend execution until
255 time has advanced by at least @w{@var{x} timer ticks}.  This is
256 useful for threads that operate in real-time, for example, for
257 blinking the cursor once per second.  There is no requirement that
258 threads start running immediately after waking up; just put them on
259 the ready queue after they have waited for approximately the right
260 amount of time.
261
262 A working implementation of this function is provided.  However, the
263 version provided is poor, because it ``busy waits,'' that is, it spins
264 in a tight loop checking the current time until the current time has
265 advanced far enough.  This is undesirable because it wastes time that
266 could potentially be used more profitably by another thread.  Your
267 solution should not busy wait.
268
269 The argument to @code{timer_sleep()} is expressed in timer ticks, not
270 in milliseconds or some other unit.
271
272 @node Problem 1-2 Join
273 @section Problem 1-2: Join
274
275 Implement @code{thread_join(tid_t)} in @file{threads/thread.c}.  There
276 is already a prototype for it in @file{threads/thread.h}, which you
277 should not change.  This function causes the currently running thread
278 to block until the thread whose thread id is passed as an argument
279 exits.  If A is the running thread and B is the argument, then we say
280 that ``A joins B'' in this case.
281
282 Incidentally, we don't use @code{struct thread *} as
283 @file{thread_join()}'s parameter type because a thread pointer is not
284 unique over time.  That is, when a thread dies, its memory may be,
285 whether immediately or much later, reused for another thread.  If
286 thread A over time had two children B and C that were stored at the
287 same address, then @code{thread_join(@r{B})} and
288 @code{thread_join(@r{C})} would be ambiguous.  Introducing a thread id
289 or @dfn{tid}, represented by type @code{tid_t}, that is intentionally
290 unique over time solves the problem.  The provided code uses an
291 @code{int} for @code{tid_t}, but you may decide you prefer to use some
292 other type.
293
294 The model for @code{thread_join()} is the @command{wait} system call
295 in Unix-like systems.  (Try reading the manpages.)  That system call
296 can only be used by a parent process to wait for a child's death.  You
297 should implement @code{thread_join()} to have the same restriction.
298 That is, a thread may only join its immediate children.
299
300 A thread need not ever be joined.  Your solution should properly free
301 all of a thread's resources, including its @code{struct thread},
302 whether it is ever joined or not, and regardless of whether the child
303 exits before or after its parent.  That is, a thread should be freed
304 exactly once in all cases.
305
306 Joining a given thread is idempotent.  That is, joining a thread T
307 multiple times is equivalent to joining it once, because T has already
308 exited at the time of the later joins.  Thus, joins on T after the
309 first should return immediately.
310
311 Calling @code{thread_join()} on an thread that is not the caller's
312 child should cause the caller to return immediately.
313
314 Consider all the ways a join can occur: nested joins (A joins B when B
315 is joined on C), multiple joins (A joins B, then A joins C), and so
316 on.  Does your join work if @code{thread_join()} is called on a thread
317 that has not yet been scheduled for the first time?  You should handle
318 all of these cases.  Write test code that demonstrates the cases your
319 join works for.  Don't overdo the output volume, please!
320
321 Be careful to program this function correctly.  You will need its
322 functionality for project 2.
323
324 Once you've implemented @code{thread_join()}, define
325 @code{THREAD_JOIN_IMPLEMENTED} in @file{constants.h}.
326 @xref{Conditional Compilation}, for more information.
327
328 @node Problem 1-3 Priority Scheduling
329 @section Problem 1-3: Priority Scheduling
330
331 Implement priority scheduling in Pintos.  Priority scheduling is a key
332 building block for real-time systems.  Implement functions
333 @code{thread_set_priority()} to set the priority of the running thread
334 and @code{thread_get_priority()} to get the running thread's priority.
335 (A thread can examine and modify only its own priority.)  There are
336 already prototypes for these functions in @file{threads/thread.h},
337 which you should not change.
338
339 Thread priority ranges from @code{PRI_MIN} (0) to @code{PRI_MAX} (59).
340 The initial thread priority is passed as an argument to
341 @code{thread_create()}.  If there's no reason to choose another
342 priority, use @code{PRI_DEFAULT} (29).  The @code{PRI_} macros are
343 defined in @file{threads/thread.h}, and you should not change their
344 values.
345
346 When a thread is added to the ready list that has a higher priority
347 than the currently running thread, the current thread should
348 immediately yield the processor to the new thread.  Similarly, when
349 threads are waiting for a lock, semaphore or condition variable, the
350 highest priority waiting thread should be woken up first.  A thread's
351 priority may be set at any time, including while the thread is waiting
352 on a lock, semaphore, or condition variable.
353
354 One issue with priority scheduling is ``priority inversion'': if a
355 high priority thread needs to wait for a low priority thread (for
356 instance, for a lock held by a low priority thread, or in
357 @code{thread_join()} for a thread to complete), and a middle priority
358 thread is on the ready list, then the high priority thread will never
359 get the CPU because the low priority thread will not get any CPU time.
360 A partial fix for this problem is to have the waiting thread
361 ``donate'' its priority to the low priority thread while it is holding
362 the lock, then recall the donation once it has acquired the lock.
363 Implement this fix.
364
365 You will need to account for all different orders that priority
366 donation and inversion can occur.  Be sure to handle multiple
367 donations, in which multiple priorities are donated to a thread.  You
368 must also handle nested donation: given high, medium, and low priority
369 threads H, M, and L, respectively, if H is waiting on a lock that M
370 holds and M is waiting on a lock that L holds, then both M and L
371 should be boosted to H's priority.
372
373 You only need to implement priority donation when a thread is waiting
374 for a lock held by a lower-priority thread.  You do not need to
375 implement this fix for semaphores, condition variables or joins.
376 However, you do need to implement priority scheduling in all cases.
377
378 @node Problem 1-4 Advanced Scheduler
379 @section Problem 1-4: Advanced Scheduler
380
381 Implement Solaris's multilevel feedback queue scheduler (MLFQS) to
382 reduce the average response time for running jobs on your system.
383 @xref{Multilevel Feedback Scheduling}, for a detailed description of
384 the MLFQS requirements.
385
386 Demonstrate that your scheduling algorithm reduces response time
387 relative to the original Pintos scheduling algorithm (round robin) for
388 at least one workload of your own design (i.e.@: in addition to the
389 provided test).
390
391 You may assume a static priority for this problem. It is not necessary
392 to ``re-donate'' a thread's priority if it changes (although you are
393 free to do so).
394
395 You must write your code so that we can turn the MLFQS on and off at
396 compile time.  By default, it must be off, but we must be able to turn
397 it on by inserting the line @code{#define MLFQS 1} in
398 @file{constants.h}.  @xref{Conditional Compilation}, for details.
399
400 @node Threads FAQ
401 @section FAQ
402
403 @enumerate 1
404 @item General FAQs
405
406 @enumerate 1
407 @item
408 @b{I am adding a new @file{.h} or @file{.c} file.  How do I fix the
409 @file{Makefile}s?}@anchor{Adding c or h Files}
410
411 To add a @file{.c} file, edit the top-level @file{Makefile.build}.
412 You'll want to add your file to variable @samp{@var{dir}_SRC}, where
413 @var{dir} is the directory where you added the file.  For this
414 project, that means you should add it to @code{threads_SRC}, or
415 possibly @code{devices_SRC} if you put in the @file{devices}
416 directory.  Then run @code{make}.  If your new file doesn't get
417 compiled, run @code{make clean} and then try again.
418
419 When you modify the top-level @file{Makefile.build}, the modified
420 version should be automatically copied to
421 @file{threads/build/Makefile} when you re-run make.  The opposite is
422 not true, so any changes will be lost the next time you run @code{make
423 clean} from the @file{threads} directory.  Therefore, you should
424 prefer to edit @file{Makefile.build} (unless your changes are meant to
425 be truly temporary).
426
427 There is no need to edit the @file{Makefile}s to add a @file{.h} file.
428
429 @item
430 @b{How do I write my test cases?}
431
432 Test cases should be replacements for the existing @file{test.c}
433 file.  Put them in a @file{threads/testcases} directory.
434 @xref{TESTCASE}, for more information.
435
436 @item
437 @b{Why can't I disable interrupts?}
438
439 Turning off interrupts should only be done for short amounts of time,
440 or else you end up losing important things such as disk or input
441 events.  Turning off interrupts also increases the interrupt handling
442 latency, which can make a machine feel sluggish if taken too far.
443 Therefore, in general, setting the interrupt level should be used
444 sparingly.  Also, any synchronization problem can be easily solved by
445 turning interrupts off, since while interrupts are off, there is no
446 concurrency, so there's no possibility for race condition.
447
448 To make sure you understand concurrency well, we are discouraging you
449 from taking this shortcut at all in your solution.  If you are unable
450 to solve a particular synchronization problem with semaphores, locks,
451 or conditions, or think that they are inadequate for a particular
452 reason, you may turn to disabling interrupts.  If you want to do this,
453 we require in your design document a complete justification and
454 scenario (i.e.@: exact sequence of events) to show why interrupt
455 manipulation is the best solution.  If you are unsure, the TAs can
456 help you determine if you are using interrupts too haphazardly.  We
457 want to emphasize that there are only limited cases where this is
458 appropriate.
459
460 You might find @file{devices/intq.h} and its users to be an
461 inspiration or source of rationale.
462
463 @item
464 @b{Where might interrupt-level manipulation be appropriate?}
465
466 You might find it necessary in some solutions to the Alarm problem.
467
468 You might want it at one small point for the priority scheduling
469 problem.  Note that it is not required to use interrupts for these
470 problems.  There are other, equally correct solutions that do not
471 require interrupt manipulation.  However, if you do manipulate
472 interrupts and @strong{correctly and fully document it} in your design
473 document, we will allow limited use of interrupt disabling.
474
475 @item
476 @b{What does ``warning: no previous prototype for `@var{function}''
477 mean?}
478
479 It means that you defined a non-@code{static} function without
480 preceding it by a prototype.  Because non-@code{static} functions are
481 intended for use by other @file{.c} files, for safety they should be
482 prototyped in a header file included before their definition.  To fix
483 the problem, add a prototype in a header file that you include, or, if
484 the function isn't actually used by other @file{.c} files, make it
485 @code{static}.
486 @end enumerate
487
488 @item Alarm Clock FAQs
489
490 @enumerate 1
491 @item
492 @b{Why can't I use most synchronization primitives in an interrupt
493 handler?}
494
495 As you've discovered, you cannot sleep in an external interrupt
496 handler.  Since many lock, semaphore, and condition variable functions
497 attempt to sleep, you won't be able to call those in
498 @code{timer_interrupt()}.  You may still use those that never sleep.
499
500 Having said that, you need to make sure that global data does not get
501 updated by multiple threads simultaneously executing
502 @code{timer_sleep()}.  Here are some pieces of information to think
503 about:
504
505 @enumerate a
506 @item
507 Interrupts are turned off while @code{timer_interrupt()} runs.  This
508 means that @code{timer_interrupt()} will not be interrupted by a
509 thread running in @code{timer_sleep()}.
510
511 @item
512 A thread in @code{timer_sleep()}, however, can be interrupted by a
513 call to @code{timer_interrupt()}, except when that thread has turned
514 off interrupts.
515
516 @item
517 Examples of synchronization mechanisms have been presented in lecture.
518 Going over these examples should help you understand when each type is
519 useful or needed.
520 @end enumerate
521
522 @item
523 @b{What about timer overflow due to the fact that times are defined as
524 integers? Do I need to check for that?}
525
526 Don't worry about the possibility of timer values overflowing.  Timer
527 values are expressed as signed 63-bit numbers, which at 100 ticks per
528 second should be good for almost 2,924,712,087 years.
529 @end enumerate
530
531 @item Join FAQs
532
533 @enumerate 1
534 @item
535 @b{Am I correct to assume that once a thread is deleted, it is no
536 longer accessible by the parent (i.e.@: the parent can't call
537 @code{thread_join(child)})?}
538
539 A parent joining a child that has completed should be handled
540 gracefully and should act as a no-op.
541 @end enumerate
542
543 @item Priority Scheduling FAQs
544
545 @enumerate 1
546 @item
547 @b{Doesn't the priority scheduling lead to starvation? Or do I have to
548 implement some sort of aging?}
549
550 It is true that strict priority scheduling can lead to starvation
551 because thread may not run if a higher-priority thread is runnable.
552 In this problem, don't worry about starvation or any sort of aging
553 technique.  Problem 4 will introduce a mechanism for dynamically
554 changing thread priorities.
555
556 This sort of scheduling is valuable in real-time systems because it
557 offers the programmer more control over which jobs get processing
558 time.  High priorities are generally reserved for time-critical
559 tasks. It's not ``fair,'' but it addresses other concerns not
560 applicable to a general-purpose operating system.
561
562 @item
563 @b{After a lock has been released, does the program need to switch to
564 the highest priority thread that needs the lock (assuming that its
565 priority is higher than that of the current thread)?}
566
567 When a lock is released, the highest priority thread waiting for that
568 lock should be unblocked and put on the ready to run list.  The
569 scheduler should then run the highest priority thread on the ready
570 list.
571
572 @item
573 @b{If a thread calls @code{thread_yield()} and then it turns out that
574 it has higher priority than any other threads, does the high-priority
575 thread continue running?}
576
577 Yes.  If there is a single highest-priority thread, it continues
578 running until it blocks or finishes, even if it calls
579 @code{thread_yield()}.
580
581 @item
582 @b{If the highest priority thread is added to the ready to run list it
583 should start execution immediately.  Is it immediate enough if I
584 wait until next timer interrupt occurs?}
585
586 The highest priority thread should run as soon as it is runnable,
587 preempting whatever thread is currently running.
588
589 @item
590 @b{What happens to the priority of the donating thread?  Do the priorities
591 get swapped?}
592
593 No.  Priority donation only changes the priority of the low-priority
594 thread.  The donating thread's priority stays unchanged.  Also note
595 that priorities aren't additive: if thread A (with priority 5) donates
596 to thread B (with priority 3), then B's new priority is 5, not 8.
597
598 @item 
599 @b{Can a thread's priority be changed while it is sitting on the ready
600 queue?}
601
602 Yes.  Consider this case: low-priority thread L currently has a lock
603 that high-priority thread H wants.  H donates its priority to L (the
604 lock holder).  L finishes with the lock, and then loses the CPU and is
605 moved to the ready queue.  Now L's old priority is restored while it
606 is in the ready queue.
607
608 @item
609 @b{Can a thread's priority change while it is sitting on the queue of a
610 semaphore?}
611
612 Yes.  Same scenario as above except L gets blocked waiting on a new
613 lock when H restores its priority.
614
615 @item
616 @b{Why is pubtest3's FIFO test skipping some threads! I know my scheduler
617 is round-robin'ing them like it's supposed to!  Our output is like this:}
618
619 @example
620 Thread 0 goes.
621 Thread 2 goes.
622 Thread 3 goes.
623 Thread 4 goes.
624 Thread 0 goes.
625 Thread 1 goes.
626 Thread 2 goes.
627 Thread 3 goes.
628 Thread 4 goes.
629 @end example
630
631 @noindent @b{which repeats 5 times and then}
632
633 @example
634 Thread 1 goes.
635 Thread 1 goes.
636 Thread 1 goes.
637 Thread 1 goes.
638 Thread 1 goes.
639 @end example
640
641 This happens because context switches are being invoked by the test
642 when it explicitly calls @code{thread_yield()}.  However, the time
643 slice timer is still alive and so, every tick (by default), thread 1
644 gets switched out (caused by @code{timer_interrupt()} calling
645 @code{intr_yield_on_return()}) before it gets a chance to run its
646 mainline.  It is by coincidence that Thread 1 is the one that gets
647 skipped in our example.  If we use a different jitter value, the same
648 behavior is seen where a thread gets started and switched out
649 completely.
650
651 Solution: Increase the value of @code{TIME_SLICE} in
652 @file{devices/timer.c} to a very high value, such as 10000, to see
653 that the threads will round-robin if they aren't interrupted.
654
655 @item
656 @b{What happens when a thread is added to the ready list which has
657 higher priority than the currently running thread?}
658
659 The correct behavior is to immediately yield the processor.  Your
660 solution must act this way.
661
662 @item
663 @b{What should @code{thread_get_priority()} return in a thread while
664 its priority has been increased by a donation?}
665
666 The higher (donated) priority.
667 @end enumerate
668
669 @item Advanced Scheduler FAQs
670
671 @enumerate 1
672 @item
673 @b{What is the interval between timer interrupts?}
674
675 Timer interrupts occur @code{TIMER_FREQ} times per second.  You can
676 adjust this value by editing @file{devices/timer.h}.  The default is
677 100 Hz.
678
679 You can also adjust the number of timer ticks per time slice by
680 modifying @code{TIME_SLICE} in @file{devices/timer.c}.
681
682 @item
683 @b{Do I have to modify the dispatch table?}
684
685 No, although you are allowed to. It is possible to complete
686 this problem (i.e.@: demonstrate response time improvement)
687 without doing so.
688
689 @item
690 @b{When the scheduler changes the priority of a thread, how does this
691 affect priority donation?}
692
693 Short (official) answer: Don't worry about it. Your priority donation
694 code may assume static priority assignment.
695
696 Longer (unofficial) opinion: If you wish to take this into account,
697 however, your design may end up being ``cleaner.''  You have
698 considerable freedom in what actually takes place. I believe what
699 makes the most sense is for scheduler changes to affect the
700 ``original'' (non-donated) priority.  This change may actually be
701 masked by the donated priority.  Priority changes should only
702 propagate with donations, not ``backwards'' from donees to donors.
703
704 @item
705 @b{What is meant by ``static priority''?}
706
707 Once thread A has donated its priority to thread B, if thread A's
708 priority changes (due to the scheduler) while the donation still
709 exists, you do not have to change thread B's donated priority.
710 However, you are free to do so.
711
712 @item
713 @b{Do I have to make my dispatch table user-configurable?}
714
715 No.  Hard-coding the dispatch table values is fine.
716 @end enumerate
717 @end enumerate