Previously we'd flush after every write. Now we only flush when the
reader has nothing more to tell us. This way we can be sure that we're
sending out the data as soon as we can, without leaving any cells
partially filled unnecessarily.
The tricks I needed here turned out to be:
* I needed to store the future returned by read_cell() while it is
returning Poll::Pending.
* Since the type of that future is secret, I needed to put it in a
Box<dyn Future>.
* But since that future holds a reference to the DataReader, that
would create a self-referential structure if we tried to store
the DataReader and the future at the same time. So instead, I
had to make an enum() that either holds a DataReader directly,
or holds the future that will give us back the DataReader when
it's done. (I also had to change the read_cell() function so
that it takes ownership of the DataReader, and returns it when
it's finished.) (Daniel Franke explained how to do this.)
* Finally, I couldn't figure out how to change the enum's type
in-place, so I had to wrap it in an Option<>. (Daniel Franke
says this isn't actually necessary.)
I've noted a couple of places in the read code where I want to
handle errors and closes more carefully.
These new (internal so far) APIs correspond more closely to what
we'll need for AsyncRead and AsyncWrite.
We also make write methods take a mutable reference to self, since
that seems to be (closer to) what the AsyncRead/AsyncWrite code
expects.
For these, we need to call tor_rtcompat::task::block_on() directly,
since they would crash with tokio enabled. Perhaps down the line we
should look for a better fix here.
This is fairly ugly and I think I'll need to mess around with the
feature configuration a while until we get something that's pleasant
to develop with. This still seems like a good idea, though, since
we _will_ need to be executor-agnostic in the end, or we'll have no
way to handle wasm or embedded environments.
Later down the road, we'll probably want to use futures::Executor or
futures::Spawn more than having global entry points in
tor_rtcompat. That would probably make our feature story simpler.
Tokio is the default now, since tokio seems to be more heavily used
for performance-critical stuff.
This patch breaks tests; the next one will fix them, albeit
questionably.