Previously I made the questionable API design of having
`StatefulLogic::snapshot` return a `StateUpdate`. This meant that the
snapshotting process affected the behavior of the stateful operator,
since it could return `Reset` to signal up to `StatefulUnary` to
discard the logic.
This unwinds that tangle by introducing a new method with a perhaps
overly poetic name `StatefulLogic::fate` which should return what
`StatefulUnary` should do with the logic when it's done processing via
a `LogicFate` enum: either `Retain` it or `Discard` it.
`fate` is attempting to encapsulate the problem of `StatefulUnary` is
the owner of the logics, so they can't drop themselves.
This is nice because it simplifies the return value of `exec` to just
output. The awaken delay is handled in `fate`.
The other part of the logic return value was "time to next
awake". Which is now encapsulated in `StatefulLogic::next_awake`.
It also breaks apart the results of `Windower` due to the same kind of
problem: the `WindowStatefulLogic` is the owner, and we need to
communicate back when it's safe to discard a `Windower`. Adds
`Windower::is_empty` and `Windower::next_close` to handle this. This
fixes the bug of never discarding window state if a key is never seen
again: we now discard that state whenever all windows for a key are
closed.
A few other small changes:
- Standardises on the language of "awaken" a logic. Still uses
Timely's "activate" for a Timely operator, though. Renames
`StatefulLogic::exec` to `StatefulLogic::on_awake` to make more
explicit when it is called. Renames `WindowLogic::exec` to
`WindowLogic::with_next` to make explicit when it's called.
- Clarifies more comments in the giant chunk of code for
`StatefulUnary::stateful_unary`. I was also able to optimize it a
little and get rid of two of our temporary buffers. I think it makes
the process slightly clearer.