    * A verbal description of the structure of your threadpool implementation, including a list of any design decisions you made.
	An instance of an Allen-Clark threadpool (ACT) is a simple data structure in C; the structure (struct) contains a mutex for preventing race conditions, an array of handles to threads, an integer to note the maximum number of threads, and an integer to keep track of the next index to work on in the array.
	The final item, the integer, was the most major design idea.  On average, each request to a web server takes the same amount of time (this holds true under real conditions; the average case is what is being considered).  Therefore, keeping a circular queue of the threads, and waiting for the next thread in the circular queue to finish seemed to be a reasonable solution to keeping the number of threads below the max, but, if necessary, at the max.  In addition, realizing that this wouldn't only be used by the web server, using a threadpool is most likely to be used as part of a 'work crew' (Birrel, "An Introduction to Programming with Threads"), so it is assumed that each thread will be placed under a similar load in a well designed software system.
	Each function also uses the pthread cleanup handler routines; these routines enable the program to recover in the event that a thread is cancelled (perhaps from a signal) in the threadpool routines while holding a mutex.
	Each thread is dispatched through a function call to a function named dispatchfunc.  This function sets the canceltype to PTHREAD_CANCEL_ANSYNCHRONOUS, and then runs the function.  This allows destroy_threadpool to work properly; each thread will be cancelled, run its cleanup handlers, and then the pthread's resources are recovered with the use of the join command.
    * A list of critical sections inside your threadpool code (and why they are critical sections).
	Each function in the threadpool guards the 'next thread to process' variable.  In general, if this variable changed while in the middle of execution, it may overwrite handles stored in the array, so the handle to particular threads may be lost.
	The destroy function is even more particular; it implicitly calls pthread_unlock_mutex in it's cleanup handler.  The biggest difficulty here is to prevent a null pointer exception in a dispatch function if two threads, A calling dispatch and B calling destroy_threadpool, are running simultaneously.
    * A description of the synchronization inside your threadpool. What condition variables did you need, where, and why? Under what conditions do threads block, and which thread is responsible for waking up a blocked thread?
	Threads in the ACT can block on I/O operations, as any thread can block.  Also, if a thread calls a threadpool operation, it will block if any other thread is also calling a threadpool operation on the same threadpool.  Multiple threadpools can exist; they do not affect eachother in any way.
	For the ACT, we did not use condition variables.  The 'best' use of condition variables seems to be that each thread should signal when it ends; if a call to dispatch is waiting for a thread to clear, it will get the signal and react accordingly.  Unfortunately, under low load conditions, a thread may send its signal without a condition variable in wait mode.  Therefore, that signal is lost, and the threadpool may not know that it has recovered a thread.
    * The graph you produced in part 3, as well as an explanation of why your graph is shaped the way it is. Try to explain all of the interesting features on your graph, if possible.
      The graph is a plot of the throughput of our server as a function of the number of threads in the thread pool.  A separate line is plotted for each value of the NUM_LOOPS variable that is passed as an argument into the server program.  During the testing of the server there were at least as many clients as there were threads in the thread pool, so for instance if there were 8 threads in the pool we had at least 8 clients running.  Our server didn't get it's timestamp until 1050 dispatches were called and then it would print out our timestamp difference after 50 more dispatches; this was done so that we didn't have inaccuracies while we were starting up clients, but all dispatches measured were when all clients had been loaded. 

