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 paging.c
139 @itemx paging.h
140 Initializes the kernel page table.  FIXME
141
142 @item malloc.c
143 @itemx malloc.h
144 A very simple implementation of @code{malloc()} and @code{free()} for
145 the kernel.  The largest block that can be allocated is 2 kB.
146
147 @item interrupt.c
148 @itemx interrupt.h
149 Basic interrupt handling and functions for turning interrupts on and
150 off.
151
152 @item intr-stubs.pl
153 @itemx intr-stubs.h
154 A Perl program that outputs assembly for low-level interrupt handling.
155
156 @item synch.c
157 @itemx synch.h
158 Basic synchronization primitives: semaphores, locks, and condition
159 variables.  You will need to use these for synchronization through all
160 four projects.
161
162 @item test.c
163 @itemx test.h
164 Test code.  For project 1, you will replace this file with your test
165 cases.
166
167 @item io.h
168 Functions for I/O port access.  This is mostly used by source code in
169 the @file{devices} directory that you won't have to touch.
170
171 @item mmu.h
172 Functions and macros related to memory management, including page
173 directories and page tables.  This will be more important to you in
174 project 3.  For now, you can ignore it.
175 @end table
176
177
178
179 @node Debugging versus Testing
180 @section Debugging versus Testing
181
182 When you're debugging code, it's useful to be able to be able to run a
183 program twice and have it do exactly the same thing.  On second and
184 later runs, you can make new observations without having to discard or
185 verify your old observations.  This property is called
186 ``reproducibility.''  The simulator we use, Bochs, can be set up for
187 reproducibility.  If you use the Bochs configuration files we provide,
188 which specify @samp{ips: @var{n}} where @var{n} is a number of
189 simulated instructions per second, your simulations can be
190 reproducible.
191
192 Of course, a simulation can only be reproducible from one run to the
193 next if its input is the same each time.  For simulating an entire
194 computer, as we do, this means that every part of the computer must be
195 the same.  For example, you must use the same disks, the same version
196 of Bochs, and you must not hit any keys on the keyboard (because you
197 could not be sure to hit them at exactly the same point each time)
198 during the runs.
199
200 While reproducibility is useful for debugging, it is a problem for
201 testing thread synchronization, an important part of this project.  In
202 particular, when Bochs is set up for reproducibility, timer interrupts
203 will come at perfectly reproducible points, and therefore so will
204 thread switches.  That means that running the same test several times
205 doesn't give you any greater confidence in your code's correctness
206 than does running it only once.
207
208 So, to make your code easier to test, we've added a feature to Bochs
209 that makes timer interrupts come at random intervals, but in a
210 perfectly predictable way.  In particular, if you put a line
211 @samp{ips-jitter: @var{seed}}, where @var{seed} is an integer, into
212 your Bochs configuration file, then timer interrupts will come at
213 irregularly spaced intervals.  Within a single @var{seed} value,
214 execution will still be reproducible, but timer behavior will change
215 as @var{seed} is varied.  Thus, for the highest degree of confidence
216 you should test your code with many seed values.
217
218 @node Tips
219 @section Tips
220
221 There should be no busy-waiting in any of your solutions to this
222 assignment.  Furthermore, resist the temptation to directly disable
223 interrupts in your solution by calling @code{intr_disable()} or
224 @code{intr_set_level()}, although you may find doing so to be useful
225 while debugging.  Instead, use semaphores, locks and condition
226 variables to solve synchronization problems.  Hint: read the comments
227 in @file{threads/synch.h} if you're unsure what synchronization
228 primitives may be used in what situations.
229
230 Given some designs of some problems, there may be one or two instances
231 in which it is appropriate to directly change the interrupt levels
232 instead of relying on the given synchroniztion primitives.  This must
233 be justified in your @file{DESIGNDOC} file.  If you're not sure you're
234 justified, ask!
235
236 While all parts of this assignment are required if you intend to earn
237 full credit on this project, keep in mind that Problem 2 (Join) will
238 be needed for future assignments, so you'll want to get this one
239 right.  We don't give out solutions, so you're stuck with your Join
240 code for the whole quarter.  Problem 1 (Alarm Clock) could be very
241 handy, but not strictly required in the future.  The upshot of all
242 this is that you should focus heavily on making sure that your
243 implementation of Join works correctly, since if it's broken, you will
244 need to fix it for future assignments.  The other parts can be turned
245 off in the future if you find you can't make them work quite right.
246
247 Also keep in mind that Problem 4 (the MLFQS) builds on the features you
248 implement in Problem 3, so to avoid unnecessary code duplication, it
249 would be a good idea to divide up the work among your team members
250 such that you have Problem 3 fully working before you begin to tackle
251 Problem 4.
252
253 @node Problem 1-1 Alarm Clock
254 @section Problem 1-1: Alarm Clock
255
256 Improve the implementation of the timer device defined in
257 @file{devices/timer.c} by reimplementing @code{timer_sleep()}.
258 Threads call @code{timer_sleep(@var{x})} to suspend execution until
259 time has advanced by at least @w{@var{x} timer ticks}.  This is
260 useful for threads that operate in real-time, for example, for
261 blinking the cursor once per second.  There is no requirement that
262 threads start running immediately after waking up; just put them on
263 the ready queue after they have waited for approximately the right
264 amount of time.
265
266 A working implementation of this function is provided.  However, the
267 version provided is poor, because it ``busy waits,'' that is, it spins
268 in a tight loop checking the current time until the current time has
269 advanced far enough.  This is undesirable because it wastes time that
270 could potentially be used more profitably by another thread.  Your
271 solution should not busy wait.
272
273 The argument to @code{timer_sleep()} is expressed in timer ticks, not
274 in milliseconds or some other unit.
275
276 @node Problem 1-2 Join
277 @section Problem 1-2: Join
278
279 Implement @code{thread_join(struct thread *)} in
280 @file{threads/thread.c}.  There is already a prototype for it in
281 @file{threads/thread.h}, which you should not change.  This function
282 causes the currently running thread to block until thread passed as an
283 argument exits.  If A is the running thread and B is the argument,
284 then we say that ``A joins B'' in this case.
285
286 The model for @code{thread_join()} is the @command{wait} system call
287 in Unix-like systems.  (Try reading the manpages.)  That system call
288 can only be used by a parent process to wait for a child's death.  You
289 should implement @code{thread_join()} to have the same restriction.
290 That is, a thread may only join on its immediate children.
291
292 A thread need not ever be joined.  Your solution should properly free
293 all of a thread's resources, including its @code{struct thread},
294 whether it is ever joined or not, and regardless of whether the child
295 exits before or after its parent.  That is, a thread should be freed
296 exactly once in all cases.
297
298 Joining a given thread is idempotent.  That is, joining a thread T
299 multiple times is equivalent to joining it once, because T has already
300 exited at the time of the later joins.  Thus, joins on T after the
301 first should return immediately.
302
303 The behavior of calling @code{thread_join()} on an thread that is not
304 the caller's child is undefined.  You need not handle this case
305 gracefully.
306
307 Consider all the ways a join can occur: nested joins (A joins B when B
308 is joined on C), multiple joins (A joins B, then A joins C), and so
309 on.  Does your join work if @code{thread_join()} is called on a thread
310 that has not yet been scheduled for the first time?  You should handle
311 all of these cases.  Write test code that demonstrates the cases your
312 join works for.  Don't overdo the output volume, please!
313
314 Be careful to program this function correctly.  You will need its
315 functionality for project 2.
316
317 @node Problem 1-3 Priority Scheduling
318 @section Problem 1-3 Priority Scheduling
319
320 Implement priority scheduling in Pintos.  Priority
321 scheduling is a key building block for real-time systems.  Implement functions
322 @code{thread_set_priority()} to set the priority of a thread and
323 @code{thread_get_priority()} to get the priority of a thread.  There
324 are already prototypes for these functions in @file{threads/thread.h},
325 which you should not change.
326
327 When a thread is added to the ready list that has a higher priority
328 than the currently running thread, the current thread should
329 immediately yield the processor to the new thread.  Similarly, when
330 threads are waiting for a lock, semaphore or condition variable, the
331 highest priority waiting thread should be woken up first.  A thread's
332 priority may be set at any time, including while the thread is waiting
333 on a lock, semaphore, or condition variable.
334
335 One issue with priority scheduling is ``priority inversion'': if a
336 high priority thread needs to wait for a low priority thread (for
337 instance, for a lock held by a low priority thread, or in
338 @code{thread_join()} for a thread to complete), and a middle priority
339 thread is on the ready list, then the high priority thread will never
340 get the CPU because the low priority thread will not get any CPU time.
341 A partial fix for this problem is to have the waiting thread
342 ``donate'' its priority to the low priority thread while it is holding
343 the lock, then recall the donation once it has acquired the lock.
344 Implement this fix.
345
346 You will need to account for all different orders that priority
347 donation and inversion can occur.  Be sure to handle multiple
348 donations, in which multiple priorities are donated to a thread.  You
349 must also handle nested donation: given high, medium, and low priority
350 threads H, M, and L, respectively, and supposing H is waiting on a
351 lock that M holds and M is waiting on a lock that L holds, both M and
352 L should be boosted to H's priority.
353
354 You only need to implement priority donation when a thread is waiting
355 for a lock held by a lower-priority thread. You do not need to
356 implement this fix for semaphores, condition variables or joins.
357 However, you do need to implement priority scheduling in all cases.
358
359 @node Problem 1-4 Advanced Scheduler
360 @section Problem 1-4 Advanced Scheduler
361
362 Implement Solaris's multilevel feedback queue scheduler (MLFQS) to
363 reduce the average response time for running jobs on your system.
364 @xref{Multilevel Feedback Scheduling}, for a detailed description of
365 the MLFQS requirements.
366
367 Demonstrate that your scheduling algorithm reduces response time
368 relative to the original Pintos scheduling algorithm (round robin) for
369 at least one workload of your own design (i.e. in addition to the
370 provided test).
371
372 You may assume a static priority for this problem. It is not necessary
373 to ``re-donate'' a thread's priority if it changes (although you are
374 free to do so).
375
376 @node Threads FAQ
377 @section FAQ
378
379 @enumerate 1
380 @item General FAQs
381
382 @enumerate 1
383 @item
384 @b{I am adding a new @file{.h} or @file{.c} file.  How do I fix the
385 @file{Makefile}s?}@anchor{Adding c or h Files}
386
387 To add a @file{.c} file, edit the top-level @file{Makefile.build}.
388 You'll want to add your file to variable @samp{@var{dir}_SRC}, where
389 @var{dir} is the directory where you added the file.  For this
390 project, that means you should add it to @code{threads_SRC}, or
391 possibly @code{devices_SRC} if you put in the @file{devices}
392 directory.  Then run @code{make}.  If your new file doesn't get
393 compiled, run @code{make clean} and then try again.
394
395 When you modify the top-level @file{Makefile.build}, the modified
396 version should be automatically copied to
397 @file{threads/build/Makefile} when you re-run make.  The opposite is
398 not true, so any changes will be lost the next time you run @code{make
399 clean} from the @file{threads} directory.  Therefore, you should
400 prefer to edit @file{Makefile.build} (unless your changes are meant to
401 be truly temporary).
402
403 There is no need to edit the @file{Makefile}s to add a @file{.h} file.
404
405 @item
406 @b{If a thread finishes, should its children be terminated immediately,
407 or should they finish normally?}
408
409 You should feel free to decide what semantics you think this
410 should have. You need only provide justification for your
411 decision.
412
413 @item
414 @b{Why can't I disable interrupts?}
415
416 Turning off interrupts should only be done for short amounts of time,
417 or else you end up losing important things such as disk or input
418 events.  Turning off interrupts also increases the interrupt handling
419 latency, which can make a machine feel sluggish if taken too far.
420 Therefore, in general, setting the interrupt level should be used
421 sparingly.  Also, any synchronization problem can be easily solved by
422 turning interrupts off, since while interrupts are off, there is no
423 concurrency, so there's no possibility for race condition.
424
425 To make sure you understand concurrency well, we are discouraging you
426 from taking this shortcut at all in your solution.  If you are unable
427 to solve a particular synchronization problem with semaphores, locks,
428 or conditions, or think that they are inadequate for a particular
429 reason, you may turn to disabling interrupts.  If you want to do this,
430 we require in your design document a complete justification and
431 scenario (i.e.@: exact sequence of events) to show why interrupt
432 manipulation is the best solution.  If you are unsure, the TAs can
433 help you determine if you are using interrupts too haphazardly.  We
434 want to emphasize that there are only limited cases where this is
435 appropriate.
436
437 @item
438 @b{Where might interrupt-level manipuation be appropriate?}
439
440 You might find it necessary in some solutions to the Alarm problem.
441
442 You might want it at one small point for the priority scheduling
443 problem.  Note that it is not required to use interrupts for these
444 problems.  There are other, equally correct solutions that do not
445 require interrupt manipulation.  However, if you do manipulate
446 interrupts and @strong{correctly and fully document it} in your design
447 document, we will allow limited use of interrupt disabling.
448 @end enumerate
449
450 @item Alarm Clock FAQs
451
452 @enumerate 1
453 @item
454 @b{Why can't I use most synchronization primitives in an interrupt
455 handler?}
456
457 As you've discovered, you cannot sleep in an external interrupt
458 handler.  Since many lock, semaphore, and condition variable functions
459 attempt to sleep, you won't be able to call those in
460 @code{timer_interrupt()}.  You may still use those that never sleep.
461
462 Having said that, you need to make sure that global data does not get
463 updated by multiple threads simultaneously executing
464 @code{timer_sleep()}.  Here are some pieces of information to think
465 about:
466
467 @enumerate a
468 @item
469 Interrupts are turned off while @code{timer_interrupt()} runs.  This
470 means that @code{timer_interrupt()} will not be interrupted by a
471 thread running in @code{timer_sleep()}.
472
473 @item
474 A thread in @code{timer_sleep()}, however, can be interrupted by a
475 call to @code{timer_interrupt()}, except when that thread has turned
476 off interrupts.
477
478 @item
479 Examples of synchronization mechanisms have been presented in lecture.
480 Going over these examples should help you understand when each type is
481 useful or needed.
482 @end enumerate
483
484 @item
485 @b{What about timer overflow due to the fact that times are defined as
486 integers? Do I need to check for that?}
487
488 Don't worry about the possibility of timer values overflowing.  Timer
489 values are expressed as signed 63-bit numbers, which at 100 ticks per
490 second should be good for almost 2,924,712,087 years.
491 @end enumerate
492
493 @item Join FAQs
494
495 @enumerate 1
496 @item
497 @b{Am I correct to assume that once a thread is deleted, it is no
498 longer accessible by the parent (i.e.@: the parent can't call
499 @code{thread_join(child)})?}
500
501 A parent joining a child that has completed should be handled
502 gracefully and should act as a no-op.
503 @end enumerate
504
505 @item Priority Scheduling FAQs
506
507 @enumerate 1
508 @item
509 @b{Doesn't the priority scheduling lead to starvation? Or do I have to
510 implement some sort of aging?}
511
512
513 It is true that strict priority scheduling can lead to starvation
514 because thread may not run if a higher-priority thread is runnable.
515 In this problem, don't worry about starvation or any sort of aging
516 technique.  Problem 4 will introduce a mechanism for dynamically
517 changing thread priorities.
518
519 This sort of scheduling is valuable in real-time systems because it
520 offers the programmer more control over which jobs get processing
521 time.  High priorities are generally reserved for time-critical
522 tasks. It's not ``fair,'' but it addresses other concerns not
523 applicable to a general-purpose operating system.
524
525 @item
526 @b{After a lock has been released, does the program need to switch to
527 the highest priority thread that needs the lock (assuming that its
528 priority is higher than that of the current thread)?}
529
530 When a lock is released, the highest priority thread waiting for that
531 lock should be unblocked and put on the ready to run list.  The
532 scheduler should then run the highest priority thread on the ready
533 list.
534
535 @item
536 @b{If a thread calls @code{thread_yield()} and then it turns out that
537 it has higher priority than any other threads, does the high-priority
538 thread continue running?}
539
540 Yes.  If there is a single highest-priority thread, it continues
541 running until it blocks or finishes, even if it calls
542 @code{thread_yield()}.
543
544 @item
545 @b{If the highest priority thread is added to the ready to run list it
546 should start execution immediately.  Is it immediate enough if I
547 wait until next timer interrupt occurs?}
548
549 The highest priority thread should run as soon as it is runnable,
550 preempting whatever thread is currently running.
551
552 @item
553 @b{What happens to the priority of the donating thread?  Do the priorities
554 get swapped?}
555
556 No.  Priority donation only changes the priority of the low-priority
557 thread.  The donating thread's priority stays unchanged.  Also note
558 that priorities aren't additive: if thread A (with priority 5) donates
559 to thread B (with priority 3), then B's new priority is 5, not 8.
560
561 @item 
562 @b{Can a thread's priority be changed while it is sitting on the ready
563 queue?}
564
565 Yes.  Consider this case: low-priority thread L currently has a lock
566 that high-priority thread H wants.  H donates its priority to L (the
567 lock holder).  L finishes with the lock, and then loses the CPU and is
568 moved to the ready queue.  Now L's old priority is restored while it
569 is in the ready queue.
570
571 @item
572 @b{Can a thread's priority change while it is sitting on the queue of a
573 semaphore?}
574
575 Yes.  Same scenario as above except L gets blocked waiting on a new
576 lock when H restores its priority.
577
578 @item
579 @b{Why is pubtest3's FIFO test skipping some threads! I know my scheduler
580 is round-robin'ing them like it's supposed to!  Our output is like this:}
581
582 @example
583 Thread 0 goes.
584 Thread 2 goes.
585 Thread 3 goes.
586 Thread 4 goes.
587 Thread 0 goes.
588 Thread 1 goes.
589 Thread 2 goes.
590 Thread 3 goes.
591 Thread 4 goes.
592 @end example
593
594 @noindent @b{which repeats 5 times and then}
595
596 @example
597 Thread 1 goes.
598 Thread 1 goes.
599 Thread 1 goes.
600 Thread 1 goes.
601 Thread 1 goes.
602 @end example
603
604 This happens because context switches are being invoked by the test
605 when it explicitly calls @code{thread_yield()}.  However, the time
606 slice timer is still alive and so, every tick (by default), thread 1
607 gets switched out (caused by @code{timer_interrupt()} calling
608 @code{intr_yield_on_return()}) before it gets a chance to run its
609 mainline.  It is by coincidence that Thread 1 is the one that gets
610 skipped in our example.  If we use a different jitter value, the same
611 behavior is seen where a thread gets started and switched out
612 completely.
613
614 Solution: Increase the value of @code{TIME_SLICE} in
615 @file{devices/timer.c} to a very high value, such as 10000, to see
616 that the threads will round-robin if they aren't interrupted.
617
618 @item
619 @b{What happens when a thread is added to the ready list which has
620 higher priority than the currently running thread?}
621
622 The correct behavior is to immediately yield the processor.  Your
623 solution must act this way.
624
625 @item
626 @b{What range of priorities should be supported and what should the
627 default priority of a thread be?}
628
629 Your implementation should support priorities from 0 through 59 and
630 the default priority of a thread should be 29.
631 @end enumerate
632
633 @item Advanced Scheduler FAQs
634
635 @enumerate 1
636 @item
637 @b{What is the interval between timer interrupts?}
638
639 Timer interrupts occur @code{TIMER_FREQ} times per second.  You can
640 adjust this value by editing @file{devices/timer.h}.  The default is
641 100 Hz.
642
643 @item
644 @b{Do I have to modify the dispatch table?}
645
646 No, although you are allowed to. It is possible to complete
647 this problem (i.e.@: demonstrate response time improvement)
648 without doing so.
649
650 @item
651 @b{When the scheduler changes the priority of a thread, how does this
652 affect priority donation?}
653
654 Short (official) answer: Don't worry about it. Your priority donation
655 code may assume static priority assignment.
656
657 Longer (unofficial) opinion: If you wish to take this into account,
658 however, your design may end up being ``cleaner.''  You have
659 considerable freedom in what actually takes place. I believe what
660 makes the most sense is for scheduler changes to affect the
661 ``original'' (non-donated) priority.  This change may actually be
662 masked by the donated priority.  Priority changes should only
663 propagate with donations, not ``backwards'' from donees to donors.
664
665 @item
666 @b{What is meant by ``static priority''?}
667
668 Once thread A has donated its priority to thread B, if thread A's
669 priority changes (due to the scheduler) while the donation still
670 exists, you do not have to change thread B's donated priority.
671 However, you are free to do so.
672
673 @item
674 @b{Do I have to make my dispatch table user-configurable?}
675
676 No.  Hard-coding the dispatch table values is fine.
677 @end enumerate
678 @end enumerate