fix: eliminate potential source of inlay hint flakiness (#10664)
This PR fixes one potential source of inlay hint flakiness. In the old `IO.waitAny` implementation, we could rely on the fact that if all tasks in the list were finished, `IO.waitAny` would pick the first finished one. In the new implementation (#9732), this isn't the case anymore for fairness reasons, but this also means that in `IO.AsyncList.getFinishedPrefixWithTimeout`, it can happen that we don't scan the full finished command snapshot prefix because we pick the timeout task before the finished snapshot task. This is likely the cause of a flaky test failure [here](https://github.com/leanprover/lean4/actions/runs/18215430028/job/51863870111), where the inlay hint test yielded no result (the timeout task has an edit delay of 0ms in the first inlay hint request that is emitted, finishes immediately and can thus immediately cause the finished prefix to be skipped with the new `waitAny` implementation). This PR fixes this issue by adding a `hasFinished` check before the `waitAny` to ensure that we always scan the finished prefix and don't need to rely on a brittle invariant that doesn't hold anymore. It also converts some `Task.get`s to `IO.wait` for safety so that the compiler can't re-order them.
This commit is contained in:
parent
b979fa012b
commit
3d75c2ce2b
2 changed files with 15 additions and 9 deletions
|
|
@ -78,7 +78,7 @@ partial def getFinishedPrefix : AsyncList ε α → BaseIO (List α × Option ε
|
|||
| nil => pure ⟨[], none, true⟩
|
||||
| delayed tl => do
|
||||
if ← tl.hasFinished then
|
||||
match tl.get with
|
||||
match ← tl.wait with
|
||||
| Except.ok tl => tl.getFinishedPrefix
|
||||
| Except.error e => pure ⟨[], some e, true⟩
|
||||
else pure ⟨[], none, false⟩
|
||||
|
|
@ -102,14 +102,18 @@ where
|
|||
return ⟨hd :: tl, e?, isComplete⟩
|
||||
| nil => return ⟨[], none, true⟩
|
||||
| delayed tl =>
|
||||
let tl : ServerTask (Except ε (AsyncList ε α)) := tl
|
||||
let tl := tl.mapCheap .inr
|
||||
let cancelTks := cancelTks.map (·.mapCheap .inl)
|
||||
let r ← ServerTask.waitAny (tl :: cancelTks ++ [timeoutTask])
|
||||
match r with
|
||||
| .inl _ => return ⟨[], none, false⟩ -- Timeout or cancellation - stop waiting
|
||||
| .inr (.ok tl) => go timeoutTask tl
|
||||
| .inr (.error e) => return ⟨[], some e, true⟩
|
||||
if ← tl.hasFinished then
|
||||
match ← tl.wait with
|
||||
| .ok tl => go timeoutTask tl
|
||||
| .error e => return ⟨[], some e, true⟩
|
||||
else
|
||||
let tl := tl.mapCheap .inr
|
||||
let cancelTks := cancelTks.map (·.mapCheap .inl)
|
||||
let r ← ServerTask.waitAny (tl :: cancelTks ++ [timeoutTask])
|
||||
match r with
|
||||
| .inl _ => return ⟨[], none, false⟩ -- Timeout or cancellation - stop waiting
|
||||
| .inr (.ok tl) => go timeoutTask tl
|
||||
| .inr (.error e) => return ⟨[], some e, true⟩
|
||||
|
||||
partial def getFinishedPrefixWithConsistentLatency (xs : AsyncList ε α) (latencyMs : UInt32)
|
||||
(cancelTks : List (ServerTask Unit) := []) : BaseIO (List α × Option ε × Bool) := do
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@ def pure (x : α) : ServerTask α := Task.pure x
|
|||
|
||||
def get (t : ServerTask α) : α := t.task.get
|
||||
|
||||
def wait (t : ServerTask α) : BaseIO α := IO.wait t.task
|
||||
|
||||
def mapCheap (f : α → β) (t : ServerTask α) : ServerTask β :=
|
||||
t.task.map f (sync := true)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue