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