finally a good implementation of produce and consume

the version we've had for a while was fast, but didn't provide fair
scheduling and was really complex.

the new version is fair, simple, and performs almost as well as the
old one in the easy case (1 consumer, producer not blocked on I/O).

the key was to isolate the function schedule_and_wait(), which allows
scheduling to be replaced with a direct yieldto() when possible. the
scheduler knows best how to do this; trying to replicate this logic
outside the scheduler is awkward.