currentRowCountTask is the one long-lived task handle on MainContentCoordinator that nothing reliably cancels. Two distinct problems, both verified in the tree.
1. teardown() does not cancel it
MainContentCoordinator.teardown() cancels every other stored task:
currentQueryTask?.cancel(); currentQueryTask = nil
refreshCoalesceTask?.cancel(); refreshCoalesceTask = nil
for entry in tableLoadTasks.values { entry.task.cancel() }
changeManagerUpdateTask?.cancel(); changeManagerUpdateTask = nil
periodicSaveTask?.cancel(); periodicSaveTask = nil
currentRowCountTask is absent. Closing a window while an exact row count is running leaves it to finish against a coordinator that is tearing down.
2. Both writers overwrite the slot without cancelling the predecessor
PaginationCoordinator.swift:170 — parent.currentRowCountTask = Task(priority: .userInitiated) { ... }
QueryExecutionCoordinator+Helpers.swift:418 — parent.currentRowCountTask = Task(priority: .utility) { ... }
Neither cancels what was already in the slot. Two loads in quick succession orphan the first task: it keeps running, and only the second one can clear the field.
The only place that does cancel it is PaginationCoordinator.cancelCurrentQuery (:133), which is Stop and refresh only.
Impact
Bounded. An exact row count is a single read, and the write-back is guarded by the content epoch introduced in #2055, so a late one cannot corrupt the grid. This is wasted server work and a task outliving its owner, not incorrect data.
Suggested fix
Cancel before assigning at both writers, and add the handle to teardown(). A tiny wrapper (setRowCountTask(_:) that cancels the previous) would make it impossible to get wrong at a third call site.
currentRowCountTaskis the one long-lived task handle onMainContentCoordinatorthat nothing reliably cancels. Two distinct problems, both verified in the tree.1.
teardown()does not cancel itMainContentCoordinator.teardown()cancels every other stored task:currentRowCountTaskis absent. Closing a window while an exact row count is running leaves it to finish against a coordinator that is tearing down.2. Both writers overwrite the slot without cancelling the predecessor
PaginationCoordinator.swift:170—parent.currentRowCountTask = Task(priority: .userInitiated) { ... }QueryExecutionCoordinator+Helpers.swift:418—parent.currentRowCountTask = Task(priority: .utility) { ... }Neither cancels what was already in the slot. Two loads in quick succession orphan the first task: it keeps running, and only the second one can clear the field.
The only place that does cancel it is
PaginationCoordinator.cancelCurrentQuery(:133), which is Stop and refresh only.Impact
Bounded. An exact row count is a single read, and the write-back is guarded by the content epoch introduced in #2055, so a late one cannot corrupt the grid. This is wasted server work and a task outliving its owner, not incorrect data.
Suggested fix
Cancel before assigning at both writers, and add the handle to
teardown(). A tiny wrapper (setRowCountTask(_:)that cancels the previous) would make it impossible to get wrong at a third call site.