Update docs.
authorBen Pfaff <blp@cs.stanford.edu>
Fri, 17 Sep 2004 06:51:46 +0000 (06:51 +0000)
committerBen Pfaff <blp@cs.stanford.edu>
Fri, 17 Sep 2004 06:51:46 +0000 (06:51 +0000)
doc/standards.texi
doc/threads.texi

index 4b3bbefc4f68dc14b0e84d00167ff8d99fe95a1c..1434a635ecdae320d66bd1a56ed9a977c2438e8c 100644 (file)
@@ -65,9 +65,10 @@ There are a few exceptions:
 
 @itemize @bullet
 @item
-Project 1 has a few parts that we must be able to turn on and off via
-conditional compilation.  You must use the macros we specify for those
-parts.
+Problem 1-4, the advanced scheduler.  We must be able to turn this on
+and off with a compile time directive.  You must use the macro name we
+specify for that part.  @xref{Problem 1-4 Advanced Scheduler}, for
+details.
 
 @item
 Code written for extra credit may be included conditionally.  If the
index 168ee79ca7e054e6a33f55a07c254becb77034f4..fbd8ac33e5c751add91b795952ffeb308fc46a79 100644 (file)
@@ -174,7 +174,7 @@ directories and page tables.  This will be more important to you in
 project 3.  For now, you can ignore it.
 @end table
 
-
+FIXME devices and lib directories?
 
 @node Debugging versus Testing
 @section Debugging versus Testing
@@ -205,6 +205,7 @@ thread switches.  That means that running the same test several times
 doesn't give you any greater confidence in your code's correctness
 than does running it only once.
 
+FIXME
 So, to make your code easier to test, we've added a feature to Bochs
 that makes timer interrupts come at random intervals, but in a
 perfectly predictable way.  In particular, if you put a line
@@ -234,21 +235,22 @@ be justified in your @file{DESIGNDOC} file.  If you're not sure you're
 justified, ask!
 
 While all parts of this assignment are required if you intend to earn
-full credit on this project, keep in mind that Problem 2 (Join) will
+full credit on this project, keep in mind that Problem 1-2 (Join) will
 be needed for future assignments, so you'll want to get this one
 right.  We don't give out solutions, so you're stuck with your Join
-code for the whole quarter.  Problem 1 (Alarm Clock) could be very
+code for the whole quarter.  Problem 1-1 (Alarm Clock) could be very
 handy, but not strictly required in the future.  The upshot of all
 this is that you should focus heavily on making sure that your
-implementation of Join works correctly, since if it's broken, you will
-need to fix it for future assignments.  The other parts can be turned
-off in the future if you find you can't make them work quite right.
+implementation of @code{thread_join()} works correctly, since if it's
+broken, you will need to fix it for future assignments.  The other
+parts can be turned off in the future if you find you can't make them
+work quite right.
 
-Also keep in mind that Problem 4 (the MLFQS) builds on the features you
-implement in Problem 3, so to avoid unnecessary code duplication, it
+Also keep in mind that Problem 1-4 (the MLFQS) builds on the features you
+implement in Problem 1-3, so to avoid unnecessary code duplication, it
 would be a good idea to divide up the work among your team members
-such that you have Problem 3 fully working before you begin to tackle
-Problem 4.
+such that you have Problem 1-3 fully working before you begin to tackle
+Problem 1-4.
 
 @node Problem 1-1 Alarm Clock
 @section Problem 1-1: Alarm Clock
@@ -276,18 +278,30 @@ in milliseconds or some other unit.
 @node Problem 1-2 Join
 @section Problem 1-2: Join
 
-Implement @code{thread_join(struct thread *)} in
-@file{threads/thread.c}.  There is already a prototype for it in
-@file{threads/thread.h}, which you should not change.  This function
-causes the currently running thread to block until thread passed as an
-argument exits.  If A is the running thread and B is the argument,
-then we say that ``A joins B'' in this case.
+Implement @code{thread_join(tid_t)} in @file{threads/thread.c}.  There
+is already a prototype for it in @file{threads/thread.h}, which you
+should not change.  This function causes the currently running thread
+to block until the thread whose thread id is passed as an argument
+exits.  If A is the running thread and B is the argument, then we say
+that ``A joins B'' in this case.
+
+Incidentally, we don't use @code{struct thread *} as
+@file{thread_join()}'s parameter type because a thread pointer is not
+unique over time.  That is, when a thread dies, its memory may be,
+whether immediately or much later, reused for another thread.  If
+thread A over time had two children B and C that were stored at the
+same address, then @code{thread_join(@r{B})} and
+@code{thread_join(@r{C})} would be ambiguous.  Introducing a thread id
+or @dfn{tid}, represented by type @code{tid_t}, that is intentionally
+unique over time solves the problem.  The provided code uses an
+@code{int} for @code{tid_t}, but you may decide you prefer to use some
+other type.
 
 The model for @code{thread_join()} is the @command{wait} system call
 in Unix-like systems.  (Try reading the manpages.)  That system call
 can only be used by a parent process to wait for a child's death.  You
 should implement @code{thread_join()} to have the same restriction.
-That is, a thread may only join on its immediate children.
+That is, a thread may only join its immediate children.
 
 A thread need not ever be joined.  Your solution should properly free
 all of a thread's resources, including its @code{struct thread},
@@ -300,9 +314,8 @@ multiple times is equivalent to joining it once, because T has already
 exited at the time of the later joins.  Thus, joins on T after the
 first should return immediately.
 
-The behavior of calling @code{thread_join()} on an thread that is not
-the caller's child is undefined.  You need not handle this case
-gracefully.
+Calling @code{thread_join()} on an thread that is not the caller's
+child should cause the caller to return immediately.
 
 Consider all the ways a join can occur: nested joins (A joins B when B
 is joined on C), multiple joins (A joins B, then A joins C), and so
@@ -315,15 +328,23 @@ Be careful to program this function correctly.  You will need its
 functionality for project 2.
 
 @node Problem 1-3 Priority Scheduling
-@section Problem 1-3 Priority Scheduling
-
-Implement priority scheduling in Pintos.  Priority
-scheduling is a key building block for real-time systems.  Implement functions
-@code{thread_set_priority()} to set the priority of a thread and
-@code{thread_get_priority()} to get the priority of a thread.  There
-are already prototypes for these functions in @file{threads/thread.h},
+@section Problem 1-3: Priority Scheduling
+
+Implement priority scheduling in Pintos.  Priority scheduling is a key
+building block for real-time systems.  Implement functions
+@code{thread_set_priority()} to set the priority of the running thread
+and @code{thread_get_priority()} to get the running thread's priority.
+(A thread can examine and modify only its own priority.)  There are
+already prototypes for these functions in @file{threads/thread.h},
 which you should not change.
 
+Thread priority ranges from @code{PRI_MIN} (0) to @code{PRI_MAX} (59).
+The initial thread priority is passed as an argument to
+@code{thread_create()}.  If there's no reason to choose another
+priority, use @code{PRI_DEFAULT} (29).  The @code{PRI_} macros are
+defined in @file{threads/thread.h}, and you should not change their
+values.
+
 When a thread is added to the ready list that has a higher priority
 than the currently running thread, the current thread should
 immediately yield the processor to the new thread.  Similarly, when
@@ -347,17 +368,17 @@ You will need to account for all different orders that priority
 donation and inversion can occur.  Be sure to handle multiple
 donations, in which multiple priorities are donated to a thread.  You
 must also handle nested donation: given high, medium, and low priority
-threads H, M, and L, respectively, and supposing H is waiting on a
-lock that M holds and M is waiting on a lock that L holds, both M and
-should be boosted to H's priority.
+threads H, M, and L, respectively, if H is waiting on a lock that M
+holds and M is waiting on a lock that L holds, then both M and L
+should be boosted to H's priority.
 
 You only need to implement priority donation when a thread is waiting
-for a lock held by a lower-priority thread. You do not need to
+for a lock held by a lower-priority thread.  You do not need to
 implement this fix for semaphores, condition variables or joins.
 However, you do need to implement priority scheduling in all cases.
 
 @node Problem 1-4 Advanced Scheduler
-@section Problem 1-4 Advanced Scheduler
+@section Problem 1-4: Advanced Scheduler
 
 Implement Solaris's multilevel feedback queue scheduler (MLFQS) to
 reduce the average response time for running jobs on your system.
@@ -366,13 +387,18 @@ the MLFQS requirements.
 
 Demonstrate that your scheduling algorithm reduces response time
 relative to the original Pintos scheduling algorithm (round robin) for
-at least one workload of your own design (i.e. in addition to the
+at least one workload of your own design (i.e.@: in addition to the
 provided test).
 
 You may assume a static priority for this problem. It is not necessary
 to ``re-donate'' a thread's priority if it changes (although you are
 free to do so).
 
+You must write your code so that we can turn the MLFQS on and off at
+compile time.  By default, it must be off, but we must be able to turn
+it on by inserting the line @code{#define MLFQS 1} in
+@file{constants.h}.  @xref{Conditional Compilation}, for details.
+
 @node Threads FAQ
 @section FAQ
 
@@ -402,6 +428,13 @@ be truly temporary).
 
 There is no need to edit the @file{Makefile}s to add a @file{.h} file.
 
+@item
+@b{How do I write my test cases?}
+
+Test cases should be replacements for the existing @file{test.c}
+file.  Put them in a @file{threads/testcases} directory.
+@xref{TESTCASE}, for more information.
+
 @item
 @b{If a thread finishes, should its children be terminated immediately,
 or should they finish normally?}
@@ -434,8 +467,11 @@ help you determine if you are using interrupts too haphazardly.  We
 want to emphasize that there are only limited cases where this is
 appropriate.
 
+You might find @file{devices/intq.h} and its users to be an
+inspiration or source of rationale.
+
 @item
-@b{Where might interrupt-level manipuation be appropriate?}
+@b{Where might interrupt-level manipulation be appropriate?}
 
 You might find it necessary in some solutions to the Alarm problem.
 
@@ -509,7 +545,6 @@ gracefully and should act as a no-op.
 @b{Doesn't the priority scheduling lead to starvation? Or do I have to
 implement some sort of aging?}
 
-
 It is true that strict priority scheduling can lead to starvation
 because thread may not run if a higher-priority thread is runnable.
 In this problem, don't worry about starvation or any sort of aging
@@ -623,11 +658,10 @@ The correct behavior is to immediately yield the processor.  Your
 solution must act this way.
 
 @item
-@b{What range of priorities should be supported and what should the
-default priority of a thread be?}
+@b{What should @code{thread_get_priority()} return in a thread while
+its priority has been increased by a donation?}
 
-Your implementation should support priorities from 0 through 59 and
-the default priority of a thread should be 29.
+The higher (donated) priority.
 @end enumerate
 
 @item Advanced Scheduler FAQs
@@ -640,6 +674,9 @@ Timer interrupts occur @code{TIMER_FREQ} times per second.  You can
 adjust this value by editing @file{devices/timer.h}.  The default is
 100 Hz.
 
+You can also adjust the number of timer ticks per time slice by
+modifying @code{TIME_SLICE} in @file{devices/timer.c}.
+
 @item
 @b{Do I have to modify the dispatch table?}