CloseAsync should always throw exceptions asynchronously.

I'm not sure how to test this case properly, although manually inserting
a 'throw new Exception()' proves that adding the relevant keywords does
actually have the intended effect. Suggestions welcome.

It's not immediately obvious that CloseAsync is performing the commits
synchronously. I suspect that 'async' and 'await' work a bit like 'yield',
in that the output format/location of the method is consistent but nothing
clever happens until the 'yield'/'await' is hit. That is to say,
everything's synchronous up until the 'await', but all exceptions will be
reliably dumped in the Task no matter where they came from. Also welcome
are suggestions on how to test this without making a mess of the code.
The background writer should wait on available work.

Spinning is not made significantly less wasteful by Task.Yield(). The
background writer should wait asynchronously on an event instead.

I couldn't find an asynchronous AutoResetEvent implementation so I wrote
one myself. Does such a thing exist? Or perhaps there's a more elegant way
of doing this?

TaskCompletionSources and locks are tricky beasts. It is an extremely
risky thing to complete the former inside the latter and usually results
in a thoroughly confusing deadlock some way down the line. For this reason,
the AwaitableAutoResetEvent never calls into the TCS while inside its lock.
Set() should not block.

SetAndWait() is really only for tests, when we wish to be sure that logic
running as a consequence of Set() has actually completed.
Add jitter to flush out suspected race condition.
Add failing test for race condition.

This is caused by isSet being used as an already-taken flag when trying to
take the event notification. The callback is triggered some time after the
Set() call, by which time Reset() has cleared the isSet flag and prevents
the event from being taken by any of its awaiters.

The fix will require that event notifications track taken-ness themselves.
Associate taken-ness of an event with its notification.
Test that existing notifications are taken by new awaiters.