1 @node Project 1--Threads, Project 2--User Programs, Pintos Tour, Top
2 @chapter Project 1: Threads
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
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.
14 Before you read the description of this project, you should read all
15 of the following sections: @ref{Introduction}, @ref{Coding Standards},
16 @ref{Project Documentation}, @ref{Debugging Tools}, and
17 @ref{Development Tools}. You should at least skim the material in
18 @ref{Threads Tour}. To complete this project you will also need to
19 read @ref{Multilevel Feedback Scheduling}.
22 * Understanding Threads::
24 * Debugging versus Testing::
26 * Problem 1-1 Alarm Clock::
28 * Problem 1-3 Priority Scheduling::
29 * Problem 1-4 Advanced Scheduler::
33 @node Understanding Threads
34 @section Understanding Threads
36 The first step is to read and understand the initial thread system.
37 Pintos, by default, implements thread creation and thread completion,
38 a simple scheduler to switch between threads, and synchronization
39 primitives (semaphores, locks, and condition variables).
41 However, there's a lot of magic going on in some of this code, so if
42 you haven't already compiled and run the base system, as described in
43 the introduction (@pxref{Introduction}), you should do so now. You
44 can read through parts of the source code by hand to see what's going
45 on. If you like, you can add calls to @func{printf} almost
46 anywhere, then recompile and run to see what happens and in what
47 order. You can also run the kernel in a debugger and set breakpoints
48 at interesting spots, single-step through code and examine data, and
49 so on. @xref{i386-elf-gdb}, for more information.
51 When a thread is created, you are creating a new context to be
52 scheduled. You provide a function to be run in this context as an
53 argument to @func{thread_create}. The first time the thread is
54 scheduled and runs, it will start from the beginning of that function
55 and execute it in the context. When that function returns, that thread
56 completes. Each thread, therefore, acts like a mini-program running
57 inside Pintos, with the function passed to @func{thread_create}
58 acting like @func{main}.
60 At any given time, Pintos is running exactly one thread, with the
61 others switched out. The scheduler decides which thread to run next
62 when it needs to switch between them. (If no thread is ready to run
63 at any given time, then the special ``idle'' thread runs.) The
64 synchronization primitives are used to force context switches when one
65 thread needs to wait for another thread to do something.
67 The exact mechanics of a context switch are pretty gruesome and have
68 been provided for you in @file{threads/switch.S} (this is 80@var{x}86
69 assembly; don't worry about understanding it). It involves saving the
70 state of the currently running thread and restoring the state of the
71 thread we're switching to.
73 Using the @command{gdb} debugger, slowly trace through a context
74 switch to see what happens (@pxref{i386-elf-gdb}). You can set a
75 breakpoint on the @func{schedule} function to start out, and then
76 single-step from there.@footnote{@command{gdb} might tell you that
77 @func{schedule} doesn't exist, which is arguably a @command{gdb} bug.
78 You can work around this by setting the breakpoint by filename and
79 line number, e.g.@: @code{break thread.c:@var{ln}} where @var{ln} is
80 the line number of the first declaration in @func{schedule}.
81 Alternatively you can recompile with optimization turned off, by
82 removing @samp{-O3} from the @code{CFLAGS} line in
83 @file{Make.config}.} Be sure to keep track of each thread's address
84 and state, and what procedures are on the call stack for each thread.
85 You will notice that when one thread calls @func{switch_threads},
86 another thread starts running, and the first thing the new thread does
87 is to return from @func{switch_threads}. We realize this comment will
88 seem cryptic to you at this point, but you will understand threads
89 once you understand why the @func{switch_threads} that gets called is
90 different from the @func{switch_threads} that returns.
92 @strong{Warning}: In Pintos, each thread is assigned a small,
93 fixed-size execution stack just under @w{4 kB} in size. The kernel
94 does try to detect stack overflow, but it cannot always succeed. You
95 may cause bizarre problems, such as mysterious kernel panics, if you
96 declare large data structures as non-static local variables,
97 e.g. @samp{int buf[1000];}. Alternatives to stack allocation include
98 the page allocator in @file{threads/palloc.c} and the block allocator
99 in @file{threads/malloc.c}. Note that the page allocator doles out
100 @w{4 kB} chunks and that @func{malloc} has a @w{2 kB} block size
101 limit. If you need larger chunks, consider using a linked structure
107 Here is a brief overview of the files in the @file{threads}
108 directory. You will not need to modify most of this code, but the
109 hope is that presenting this overview will give you a start on what
115 The kernel loader. Assembles to 512 bytes of code and data that the
116 PC BIOS loads into memory and which in turn loads the kernel into
117 memory, does basic processor initialization, and jumps to the
118 beginning of the kernel. You should not need to look at this code or
122 The linker script used to link the kernel. Sets the load address of
123 the kernel and arranges for @file{start.S} to be at the very beginning
124 of the kernel image. Again, you should not need to look at this code
125 or modify it, but it's here in case you're curious.
128 Jumps to @func{main}.
132 Kernel initialization, including @func{main}, the kernel's ``main
133 program.'' You should look over @func{main} at least to see what
138 Basic thread support. Much of your work will take place in these
139 files. @file{thread.h} defines @struct{thread}, which you will
140 modify in the first three projects.
144 Assembly language routine for switching threads. Already discussed
149 Page allocator, which hands out system memory in multiples of 4 kB
154 A very simple implementation of @func{malloc} and @func{free} for
159 Basic interrupt handling and functions for turning interrupts on and
164 A Perl program that outputs assembly for low-level interrupt handling.
168 Basic synchronization primitives: semaphores, locks, and condition
169 variables. You will need to use these for synchronization through all
174 Test code. For project 1, you will replace this file with your test
178 Functions for I/O port access. This is mostly used by source code in
179 the @file{devices} directory that you won't have to touch.
182 Functions and macros related to memory management, including page
183 directories and page tables. This will be more important to you in
184 project 3. For now, you can ignore it.
193 @subsection @file{devices} code
195 The basic threaded kernel also includes these files in the
196 @file{devices} directory:
201 System timer that ticks, by default, 100 times per second. You will
202 modify this code in Problem 1-1.
206 VGA display driver. Responsible for writing text to the screen.
207 You should have no need to look at this code. @func{printf} will
208 call into the VGA display driver for you, so there's little reason to
209 call this code yourself.
213 Serial port driver. Again, @func{printf} calls this code for you,
214 so you don't need to do so yourself. Feel free to look through it if
219 Supports reading and writing sectors on up to 4 IDE disks. This won't
220 actually be used until project 2.
224 Interrupt queue, for managing a circular queue that both kernel
225 threads and interrupt handlers want to access. Used by the keyboard
230 @subsection @file{lib} files
232 Finally, @file{lib} and @file{lib/kernel} contain useful library
233 routines. (@file{lib/user} will be used by user programs, starting in
234 project 2, but it is not part of the kernel.) Here's a few more
251 Implementation of the standard C library. @xref{C99}, for information
252 on a few recently introduced pieces of the C library that you might
253 not have encountered before. @xref{Unsafe String Functions}, for
254 information on what's been intentionally left out for safety.
258 Functions and macros to aid debugging. @xref{Debugging Tools}, for
263 Pseudo-random number generator.
269 System call numbers. Not used until project 2.
273 Doubly linked list implementation. Used all over the Pintos code, and
274 you'll probably want to use it a few places yourself in project 1.
276 @item kernel/bitmap.c
277 @itemx kernel/bitmap.h
278 Bitmap implementation. You can use this in your code if you like, but
279 you probably won't have any need for project 1.
283 Hash table implementation. Likely to come in handy for project 3.
285 @item kernel/console.c
286 @itemx kernel/console.h
287 Implements @func{printf} and a few other functions.
290 @node Debugging versus Testing
291 @section Debugging versus Testing
293 When you're debugging code, it's useful to be able to be able to run a
294 program twice and have it do exactly the same thing. On second and
295 later runs, you can make new observations without having to discard or
296 verify your old observations. This property is called
297 ``reproducibility.'' The simulator we use, Bochs, can be set up for
298 reproducibility, and that's the way that @command{pintos} invokes it.
300 Of course, a simulation can only be reproducible from one run to the
301 next if its input is the same each time. For simulating an entire
302 computer, as we do, this means that every part of the computer must be
303 the same. For example, you must use the same disks, the same version
304 of Bochs, and you must not hit any keys on the keyboard (because you
305 could not be sure to hit them at exactly the same point each time)
308 While reproducibility is useful for debugging, it is a problem for
309 testing thread synchronization, an important part of this project. In
310 particular, when Bochs is set up for reproducibility, timer interrupts
311 will come at perfectly reproducible points, and therefore so will
312 thread switches. That means that running the same test several times
313 doesn't give you any greater confidence in your code's correctness
314 than does running it only once.
316 So, to make your code easier to test, we've added a feature to Bochs
317 that makes timer interrupts come at random intervals, but in a
318 perfectly predictable way. In particular, if you invoke
319 @command{pintos} with the option @option{-j @var{seed}}, timer
320 interrupts will come at irregularly spaced intervals. Within a single
321 @var{seed} value, execution will still be reproducible, but timer
322 behavior will change as @var{seed} is varied. Thus, for the highest
323 degree of confidence you should test your code with many seed values.
328 There should be no busy-waiting in any of your solutions to this
329 assignment. Furthermore, resist the temptation to directly disable
330 interrupts in your solution by calling @func{intr_disable} or
331 @func{intr_set_level}, although you may find doing so to be useful
332 while debugging. Instead, use semaphores, locks and condition
333 variables to solve synchronization problems. Hint: read the comments
334 in @file{threads/synch.h} if you're unsure what synchronization
335 primitives may be used in what situations.
337 Given some designs of some problems, there may be one or two instances
338 in which it is appropriate to directly change the interrupt levels
339 instead of relying on the given synchroniztion primitives. This must
340 be justified in your @file{DESIGNDOC} file. If you're not sure you're
343 While all parts of this assignment are required if you intend to earn
344 full credit on this project, keep in mind that Problem 1-2 (Join) will
345 be needed for future assignments, so you'll want to get this one
346 right. We don't give out solutions, so you're stuck with your Join
347 code for the whole quarter. Problem 1-1 (Alarm Clock) could be very
348 handy, but not strictly required in the future. The upshot of all
349 this is that you should focus heavily on making sure that your
350 implementation of @func{thread_join} works correctly, since if it's
351 broken, you will need to fix it for future assignments. The other
352 parts can be turned off in the future if you find you can't make them
355 Also keep in mind that Problem 1-4 (the MLFQS) builds on the features you
356 implement in Problem 1-3, so to avoid unnecessary code duplication, it
357 would be a good idea to divide up the work among your team members
358 such that you have Problem 1-3 fully working before you begin to tackle
361 @node Problem 1-1 Alarm Clock
362 @section Problem 1-1: Alarm Clock
364 Improve the implementation of the timer device defined in
365 @file{devices/timer.c} by reimplementing @func{timer_sleep}.
366 Threads call @code{timer_sleep(@var{x})} to suspend execution until
367 time has advanced by at least @w{@var{x} timer ticks}. This is
368 useful for threads that operate in real-time, for example, for
369 blinking the cursor once per second. There is no requirement that
370 threads start running immediately after waking up; just put them on
371 the ready queue after they have waited for approximately the right
374 A working implementation of this function is provided. However, the
375 version provided is poor, because it ``busy waits,'' that is, it spins
376 in a tight loop checking the current time until the current time has
377 advanced far enough. This is undesirable because it wastes time that
378 could potentially be used more profitably by another thread. Your
379 solution should not busy wait.
381 The argument to @func{timer_sleep} is expressed in timer ticks, not
382 in milliseconds or another unit. There are @code{TIMER_FREQ} timer
383 ticks per second, where @code{TIMER_FREQ} is a macro defined in
384 @code{devices/timer.h}.
386 @node Problem 1-2 Join
387 @section Problem 1-2: Join
389 Implement @code{thread_join(tid_t)} in @file{threads/thread.c}. There
390 is already a prototype for it in @file{threads/thread.h}, which you
391 should not change. This function causes the currently running thread
392 to block until the thread whose thread id is passed as an argument
393 exits. If @var{A} is the running thread and @var{B} is the argument,
394 then we say that ``@var{A} joins @var{B}.''
396 Incidentally, we don't use @code{struct thread *} as
397 @func{thread_join}'s parameter type because a thread pointer is not
398 unique over time. That is, when a thread dies, its memory may be,
399 whether immediately or much later, reused for another thread. If
400 thread A over time had two children B and C that were stored at the
401 same address, then @code{thread_join(@var{B})} and
402 @code{thread_join(@var{C})} would be ambiguous. Introducing a thread
403 id or @dfn{tid}, represented by type @code{tid_t}, that is
404 intentionally unique over time solves the problem. The provided code
405 uses an @code{int} for @code{tid_t}, but you may decide you prefer to
408 The model for @func{thread_join} is the @command{wait} system call
409 in Unix-like systems. (Try reading the manpages.) That system call
410 can only be used by a parent process to wait for a child's death. You
411 should implement @func{thread_join} to have the same restriction.
412 That is, a thread may only join its immediate children.
414 A thread need not ever be joined. Your solution should properly free
415 all of a thread's resources, including its @struct{thread},
416 whether it is ever joined or not, and regardless of whether the child
417 exits before or after its parent. That is, a thread should be freed
418 exactly once in all cases.
420 Joining a given thread is idempotent. That is, joining a thread T
421 multiple times is equivalent to joining it once, because T has already
422 exited at the time of the later joins. Thus, joins on T after the
423 first should return immediately.
425 Calling @func{thread_join} on an thread that is not the caller's
426 child should cause the caller to return immediately.
428 Consider all the ways a join can occur: nested joins (@var{A} joins
429 @var{B}, then @var{B} joins @var{C}), multiple joins (@var{A} joins
430 @var{B}, then @var{A} joins @var{C}), and so on. Does your join work
431 if @func{thread_join} is called on a thread that has not yet been
432 scheduled for the first time? You should handle all of these cases.
433 Write test code that demonstrates the cases your join works for.
434 Don't overdo the output volume, please!
436 Be careful to program this function correctly. You will need its
437 functionality for project 2.
439 Once you've implemented @func{thread_join}, define
440 @code{THREAD_JOIN_IMPLEMENTED} in @file{constants.h}.
441 @xref{Conditional Compilation}, for more information.
443 @node Problem 1-3 Priority Scheduling
444 @section Problem 1-3: Priority Scheduling
446 Implement priority scheduling in Pintos. Priority scheduling is a key
447 building block for real-time systems. Implement functions
448 @func{thread_set_priority} to set the priority of the running thread
449 and @func{thread_get_priority} to get the running thread's priority.
450 (A thread can examine and modify only its own priority.) There are
451 already prototypes for these functions in @file{threads/thread.h},
452 which you should not change.
454 Thread priority ranges from @code{PRI_MIN} (0) to @code{PRI_MAX} (59).
455 The initial thread priority is passed as an argument to
456 @func{thread_create}. If there's no reason to choose another
457 priority, use @code{PRI_DEFAULT} (29). The @code{PRI_} macros are
458 defined in @file{threads/thread.h}, and you should not change their
461 When a thread is added to the ready list that has a higher priority
462 than the currently running thread, the current thread should
463 immediately yield the processor to the new thread. Similarly, when
464 threads are waiting for a lock, semaphore or condition variable, the
465 highest priority waiting thread should be woken up first. A thread
466 may set its priority at any time.
468 One issue with priority scheduling is ``priority inversion'': if a
469 high priority thread needs to wait for a low priority thread (for
470 instance, for a lock held by a low priority thread, or in
471 @func{thread_join} for a thread to complete), and a middle priority
472 thread is on the ready list, then the high priority thread will never
473 get the CPU because the low priority thread will not get any CPU time.
474 A partial fix for this problem is to have the waiting thread
475 ``donate'' its priority to the low priority thread while it is holding
476 the lock, then recall the donation once it has acquired the lock.
479 You will need to account for all different orders that priority
480 donation and inversion can occur. Be sure to handle multiple
481 donations, in which multiple priorities are donated to a thread. You
482 must also handle nested donation: given high, medium, and low priority
483 threads @var{H}, @var{M}, and @var{L}, respectively, if @var{H} is
484 waiting on a lock that @var{M} holds and @var{M} is waiting on a lock
485 that @var{L} holds, then both @var{M} and @var{L} should be boosted to
488 You only need to implement priority donation when a thread is waiting
489 for a lock held by a lower-priority thread. You do not need to
490 implement this fix for semaphores, condition variables or joins.
491 However, you do need to implement priority scheduling in all cases.
493 @node Problem 1-4 Advanced Scheduler
494 @section Problem 1-4: Advanced Scheduler
496 Implement Solaris's multilevel feedback queue scheduler (MLFQS) to
497 reduce the average response time for running jobs on your system.
498 @xref{Multilevel Feedback Scheduling}, for a detailed description of
499 the MLFQS requirements.
501 Demonstrate that your scheduling algorithm reduces response time
502 relative to the original Pintos scheduling algorithm (round robin) for
503 at least one workload of your own design (i.e.@: in addition to the
506 You may assume a static priority for this problem. It is not necessary
507 to ``re-donate'' a thread's priority if it changes (although you are
510 You must write your code so that we can turn the MLFQS on and off at
511 compile time. By default, it must be off, but we must be able to turn
512 it on by inserting the line @code{#define MLFQS 1} in
513 @file{constants.h}. @xref{Conditional Compilation}, for details.
520 @b{I am adding a new @file{.h} or @file{.c} file. How do I fix the
521 @file{Makefile}s?}@anchor{Adding c or h Files}
523 To add a @file{.c} file, edit the top-level @file{Makefile.build}.
524 You'll want to add your file to variable @samp{@var{dir}_SRC}, where
525 @var{dir} is the directory where you added the file. For this
526 project, that means you should add it to @code{threads_SRC}, or
527 possibly @code{devices_SRC} if you put in the @file{devices}
528 directory. Then run @code{make}. If your new file doesn't get
529 compiled, run @code{make clean} and then try again.
531 When you modify the top-level @file{Makefile.build}, the modified
532 version should be automatically copied to
533 @file{threads/build/Makefile} when you re-run make. The opposite is
534 not true, so any changes will be lost the next time you run @code{make
535 clean} from the @file{threads} directory. Therefore, you should
536 prefer to edit @file{Makefile.build} (unless your changes are meant to
539 There is no need to edit the @file{Makefile}s to add a @file{.h} file.
542 @b{How do I write my test cases?}
544 Test cases should be replacements for the existing @file{test.c}
545 file. Put them in a @file{threads/testcases} directory.
546 @xref{TESTCASE}, for more information.
549 @b{Why can't I disable interrupts?}
551 Turning off interrupts should only be done for short amounts of time,
552 or else you end up losing important things such as disk or input
553 events. Turning off interrupts also increases the interrupt handling
554 latency, which can make a machine feel sluggish if taken too far.
555 Therefore, in general, setting the interrupt level should be used
556 sparingly. Also, any synchronization problem can be easily solved by
557 turning interrupts off, since while interrupts are off, there is no
558 concurrency, so there's no possibility for race condition.
560 To make sure you understand concurrency well, we are discouraging you
561 from taking this shortcut at all in your solution. If you are unable
562 to solve a particular synchronization problem with semaphores, locks,
563 or conditions, or think that they are inadequate for a particular
564 reason, you may turn to disabling interrupts. If you want to do this,
565 we require in your design document a complete justification and
566 scenario (i.e.@: exact sequence of events) to show why interrupt
567 manipulation is the best solution. If you are unsure, the TAs can
568 help you determine if you are using interrupts too haphazardly. We
569 want to emphasize that there are only limited cases where this is
572 You might find @file{devices/intq.h} and its users to be an
573 inspiration or source of rationale.
576 @b{Where might interrupt-level manipulation be appropriate?}
578 You might find it necessary in some solutions to the Alarm problem.
580 You might want it at one small point for the priority scheduling
581 problem. Note that it is not required to use interrupts for these
582 problems. There are other, equally correct solutions that do not
583 require interrupt manipulation. However, if you do manipulate
584 interrupts and @strong{correctly and fully document it} in your design
585 document, we will allow limited use of interrupt disabling.
588 @b{What does ``warning: no previous prototype for `@var{function}''
591 It means that you defined a non-@code{static} function without
592 preceding it by a prototype. Because non-@code{static} functions are
593 intended for use by other @file{.c} files, for safety they should be
594 prototyped in a header file included before their definition. To fix
595 the problem, add a prototype in a header file that you include, or, if
596 the function isn't actually used by other @file{.c} files, make it
601 * Problem 1-1 Alarm Clock FAQ::
602 * Problem 1-2 Join FAQ::
603 * Problem 1-3 Priority Scheduling FAQ::
604 * Problem 1-4 Advanced Scheduler FAQ::
607 @node Problem 1-1 Alarm Clock FAQ
608 @subsection Problem 1-1: Alarm Clock FAQ
612 @b{Why can't I use most synchronization primitives in an interrupt
615 As you've discovered, you cannot sleep in an external interrupt
616 handler. Since many lock, semaphore, and condition variable functions
617 attempt to sleep, you won't be able to call those in
618 @func{timer_interrupt}. You may still use those that never sleep.
620 Having said that, you need to make sure that global data does not get
621 updated by multiple threads simultaneously executing
622 @func{timer_sleep}. Here are some pieces of information to think
627 Interrupts are turned off while @func{timer_interrupt} runs. This
628 means that @func{timer_interrupt} will not be interrupted by a
629 thread running in @func{timer_sleep}.
632 A thread in @func{timer_sleep}, however, can be interrupted by a
633 call to @func{timer_interrupt}, except when that thread has turned
637 Examples of synchronization mechanisms have been presented in lecture.
638 Going over these examples should help you understand when each type is
643 @b{What about timer overflow due to the fact that times are defined as
644 integers? Do I need to check for that?}
646 Don't worry about the possibility of timer values overflowing. Timer
647 values are expressed as signed 63-bit numbers, which at 100 ticks per
648 second should be good for almost 2,924,712,087 years.
651 @node Problem 1-2 Join FAQ
652 @subsection Problem 1-2: Join FAQ
656 @b{Am I correct to assume that once a thread is deleted, it is no
657 longer accessible by the parent (i.e.@: the parent can't call
658 @code{thread_join(child)})?}
660 A parent joining a child that has completed should be handled
661 gracefully and should act as a no-op.
664 @node Problem 1-3 Priority Scheduling FAQ
665 @subsection Problem 1-3: Priority Scheduling FAQ
669 @b{Doesn't the priority scheduling lead to starvation? Or do I have to
670 implement some sort of aging?}
672 It is true that strict priority scheduling can lead to starvation
673 because thread may not run if a higher-priority thread is runnable.
674 In this problem, don't worry about starvation or any sort of aging
675 technique. Problem 4 will introduce a mechanism for dynamically
676 changing thread priorities.
678 This sort of scheduling is valuable in real-time systems because it
679 offers the programmer more control over which jobs get processing
680 time. High priorities are generally reserved for time-critical
681 tasks. It's not ``fair,'' but it addresses other concerns not
682 applicable to a general-purpose operating system.
685 @b{After a lock has been released, does the program need to switch to
686 the highest priority thread that needs the lock (assuming that its
687 priority is higher than that of the current thread)?}
689 When a lock is released, the highest priority thread waiting for that
690 lock should be unblocked and put on the ready to run list. The
691 scheduler should then run the highest priority thread on the ready
695 @b{If a thread calls @func{thread_yield} and then it turns out that
696 it has higher priority than any other threads, does the high-priority
697 thread continue running?}
699 Yes. If there is a single highest-priority thread, it continues
700 running until it blocks or finishes, even if it calls
704 @b{If the highest priority thread is added to the ready to run list it
705 should start execution immediately. Is it immediate enough if I
706 wait until next timer interrupt occurs?}
708 The highest priority thread should run as soon as it is runnable,
709 preempting whatever thread is currently running.
712 @b{What happens to the priority of the donating thread? Do the priorities
715 No. Priority donation only changes the priority of the low-priority
716 thread. The donating thread's priority stays unchanged. Also note
717 that priorities aren't additive: if thread A (with priority 5) donates
718 to thread B (with priority 3), then B's new priority is 5, not 8.
721 @b{Can a thread's priority be changed while it is sitting on the ready
724 Yes. Consider this case: low-priority thread L currently has a lock
725 that high-priority thread H wants. H donates its priority to L (the
726 lock holder). L finishes with the lock, and then loses the CPU and is
727 moved to the ready queue. Now L's old priority is restored while it
728 is in the ready queue.
731 @b{Can a thread's priority change while it is sitting on the queue of a
734 Yes. Same scenario as above except L gets blocked waiting on a new
735 lock when H restores its priority.
738 @b{Why is pubtest3's FIFO test skipping some threads! I know my scheduler
739 is round-robin'ing them like it's supposed to! Our output is like this:}
753 @noindent @b{which repeats 5 times and then}
763 This happens because context switches are being invoked by the test
764 when it explicitly calls @func{thread_yield}. However, the time
765 slice timer is still alive and so, every tick (by default), thread 1
766 gets switched out (caused by @func{timer_interrupt} calling
767 @func{intr_yield_on_return}) before it gets a chance to run its
768 mainline. It is by coincidence that Thread 1 is the one that gets
769 skipped in our example. If we use a different jitter value, the same
770 behavior is seen where a thread gets started and switched out
773 Solution: Increase the value of @code{TIME_SLICE} in
774 @file{devices/timer.c} to a very high value, such as 10000, to see
775 that the threads will round-robin if they aren't interrupted.
778 @b{What happens when a thread is added to the ready list which has
779 higher priority than the currently running thread?}
781 The correct behavior is to immediately yield the processor. Your
782 solution must act this way.
785 @b{What should @func{thread_get_priority} return in a thread while
786 its priority has been increased by a donation?}
788 The higher (donated) priority.
791 @node Problem 1-4 Advanced Scheduler FAQ
792 @subsection Problem 1-4: Advanced Scheduler FAQ
796 @b{What is the interval between timer interrupts?}
798 Timer interrupts occur @code{TIMER_FREQ} times per second. You can
799 adjust this value by editing @file{devices/timer.h}. The default is
802 You can also adjust the number of timer ticks per time slice by
803 modifying @code{TIME_SLICE} in @file{devices/timer.c}.
806 @b{Do I have to modify the dispatch table?}
808 No, although you are allowed to. It is possible to complete
809 this problem (i.e.@: demonstrate response time improvement)
813 @b{When the scheduler changes the priority of a thread, how does this
814 affect priority donation?}
816 Short (official) answer: Don't worry about it. Your priority donation
817 code may assume static priority assignment.
819 Longer (unofficial) opinion: If you wish to take this into account,
820 however, your design may end up being ``cleaner.'' You have
821 considerable freedom in what actually takes place. I believe what
822 makes the most sense is for scheduler changes to affect the
823 ``original'' (non-donated) priority. This change may actually be
824 masked by the donated priority. Priority changes should only
825 propagate with donations, not ``backwards'' from donees to donors.
828 @b{What is meant by ``static priority''?}
830 Once thread A has donated its priority to thread B, if thread A's
831 priority changes (due to the scheduler) while the donation still
832 exists, you do not have to change thread B's donated priority.
833 However, you are free to do so.
836 @b{Do I have to make my dispatch table user-configurable?}
838 No. Hard-coding the dispatch table values is fine.