All non-advanced scheduling tests are passing.

However, in the process of getting there, I've noticed what I think are
some pretty big design flaws with my current approach for priority
donation. In particular, I've placed the burden of handling donation on
the donor, when I think this burden should be on the donee. The trouble
with my approach is that the donor has to find all queues on which the
donee is waiting, and shift the position of the donor to the appropriate
place. While this has the nice property that we can seamlessly switch in
max heaps in place of the current queues, the trouble is that,
sometimes, we don't have knowledge of all the queues the donee is
sitting on. In particular, I don't know of a good way to bubble up
people waiting on condition variables, since the only queue directly
accessible from the thread is the semaphore on which thread is waiting,
which has no way to reference the condition variable in which the
semaphore is queued. One potential workaround is to do away with using
"semaphore_elem" objects to encapsulate list stuff and put a list_elem
directly in the semaphore, but then we need to also place a thread
pointer in the semaphore to avoid the chicken-egg problem described in
comments near semaphore_elem, and furthermore we still may need to deal
with all sorts of weird interrupt situations in lock_acquire() and
potentially elsewhere when using this approach.

Instead, I think it makes much more sense to shift the burden of
priority donation on the donee, by iterating over entire queues and looking
for the element with max priority whenever we want to pop something.
Note that we could use the approach in the first paragraph (popping
head elements rather than searching for all expired times) with the timer,
but in that case things were much simpler since nothing could step in and
change the TIMER_END time. Here, the situation is different.