`PanicGuard` used to own the memo so that, in the case of panic,
we could reinstall the old value -- but there's no reason for us to
do that. It's just as good to clear the slot in that case and recompute
it later. Also, it makes the code nicer to remove it, since
it allows us to have more precision about where we know the memo is
not null.
My motivation though is to work towards "partial cycle recovery".
We need a clean and easy way to cancel the ongoing execution and reset
the slot to "not computed" (turns out we used that in
`maybe_changed_since` too!).
We still record the same dependencies (or else the tests fail,
so +1 for test coverage).
This has the immediate advantage that we don't invoke the fallback
function twice for the repeated node in the cycle.
Also, fix a bug where revalidating cycles could lead to a
CycleParticipant error that is not caught (added a test for it).
We used to store a changed-at/durability that reflected only
the current frame in a cycle -- but really we are dependent
across the entire cycle, so we now store the max changed-at and
min durability from the entire thing.
It turns out this is necessary, as this test reveals!
If we don't panic, you might encounter further cycles
that aren't supposed to have executed. (Prior to these changes,
this test was panicking from the second cycle.)
The `Cancelled` struct now reflects multiple potential reasons
for a query execution to be cancelled, including unexpected cycles.
It also gives information about the participants that lacked
recovery information.
Before we could not observe the case where:
* thread A is blocked on B
* cycle detected in thread B
* some participants are on thread A and have to be marked
(In particular, I commented out some code that seemed necessary and
didn't see any tests fail)
If I am not in an error, all current tests uses "static" cycles -- a
cycle always present. Let's spice that up by adding conditional cycles:
cycles that appear only for specific impls