-@node README
-@section @file{README}
-
-This is the easiest of the bunch. It's the document we'll read while
-trying to get your assignment to run. First, place all of your names
-and Leland IDs (usernames) at the top. Next, you should also explain
-any quirks with your program, such as known show-stopper bugs, weird
-dependencies, and so on.
-
-If you added extra credit features to your project, explain them
-concisely in the @file{README}. Otherwise, we're likely to miss them.
-
-@node DESIGNDOC
-@section @file{DESIGNDOC}
-
-This file is our primary tool for assessing your design. Therefore,
-you should be certain to explain your design in some decent amount of
-detail. As a broad rule of thumb, we should be able to explain what
-basic data structures and algorithms you used to solve the problem
-after reading the @file{DESIGNDOC}, but without looking at the code.
-
-The easiest way to present your @file{DESIGNDOC} is to break it down
-by parts of the project (e.g.@: for project 1, you can break the
-@file{DESIGNDOC} into four parts). For each part, you should describe
-the functionality that needs to be added, the data structures and
-algorithms used to solve that problem from a high level, and the
-motivations for each decision/change you make to the code. Your
-@file{DESIGNDOC} needs to explain and justify your design. That is,
-we should be able to understand what you did, and why you chose to do
-it that way. The ``why'' should be in concrete terms of greater speed,
-better space efficiency, cleaner code, or some other real measure of
-goodness.
-
-Things you @emph{don't} need: an explanation of the pre-existing
-Pintos code, an explanation of the project spec, justification for the
-project (e.g.@: we don't need you to explain to us why filesystems are
-important to an operating system), a play-by-play of every change you
-made to the system, any other pontificating. (You may laugh at some
-of the things listed above, but we've gotten all of them in the past.)
-The @file{DESIGNDOC} needs to discuss design decisions and trade-offs
-and the rationales behind them. Any other things can be left out, to
-save your time and ours.
-
-Finally, please keep your @file{DESIGNDOC} as short as you can,
-without sacrificing key design decisions. You should be brief, yet
-complete. We don't want to read novels.
-
-@node TESTCASE
-@section @file{TESTCASE}
-
-The @file{TESTCASE} file should contain all the information about how
-you tested your programs. At minimum, this file should contain the
-output from all the tests that we've provided, with any explanations
-needed to reproduce the output (arguments to the program, turning
-features on the program on or off, and so on).
+@node Sample Assignment
+@section Sample Assignment
+
+Implement @func{thread_join}.
+
+@deftypefun void thread_join (tid_t @var{tid})
+Blocks the current thread until thread @var{tid} exits. If @var{A} is
+the running thread and @var{B} is the argument, then we say that
+``@var{A} joins @var{B}.''
+
+Incidentally, the argument is a thread id, instead of a thread pointer,
+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 @var{A} over time had two children
+@var{B} and @var{C} that were stored at the same address, then
+@code{thread_join(@var{B})} and @code{thread_join(@var{C})} would be
+ambiguous.
+
+A thread may only join its immediate children. Calling
+@func{thread_join} on a thread that is not the caller's child should
+cause the caller to return immediately. Children are not ``inherited,''
+that is, if @var{A} has child @var{B} and @var{B} has child @var{C},
+then @var{A} always returns immediately should it try to join @var{C},
+even if @var{B} is dead.
+
+A thread need not ever be joined. Your solution should properly free
+all of a thread's resources, including its @struct{thread},
+whether it is ever joined or not, and regardless of whether the child
+exits before or after its parent. That is, a thread should be freed
+exactly once in all cases.
+
+Joining a given thread is idempotent. That is, joining a thread
+multiple times is equivalent to joining it once, because it has already
+exited at the time of the later joins. Thus, joins on a given thread
+after the first should return immediately.
+
+You must handle all the ways a join can occur: nested joins (@var{A}
+joins @var{B}, then @var{B} joins @var{C}), multiple joins (@var{A}
+joins @var{B}, then @var{A} joins @var{C}), and so on.
+@end deftypefun
+
+@node Sample Design Document
+@section Sample Design Document