mirror of
https://github.com/rmcrackan/Libation.git
synced 2026-09-18 09:17:23 -04:00
Abort is a statement about the run, not about the one book being asked about, so it now becomes the session answer whether or not "apply to all" was ticked. Without that, a user who aborts with three books in flight is asked the same question by each of the others, and the run they just stopped keeps prompting. That makes every book in flight arrive at the abort path, which the previous shape could not survive: each one called CancelAllAsync, so every book asked every other book to cancel. Only the book that claims the abort tears the queue down now; the rest were cancelled by it, and report Cancelled rather than each claiming an abort of its own. The claim is made as soon as the result is known, before the queue is touched, which also keeps small the window in which the dispatch loop can start another book - one starting after CancelAllAsync snapshots the active list would outlive the abort. The window is not closed, only narrowed; it needs a book to finish in the same instant as the abort, and the queue is cleared behind it. abortCts was a CancellationTokenSource whose token was never passed to anything - only Cancel() and IsCancellationRequested were ever used, which read as though cancellation reached the book tasks when it does not. It is a plain flag now, written under the result lock and read by the loop, and says what it means. _resultLock was a local named like a field. Also adds the seam the loop is tested through: ProcessBookHandler is the single call the dispatch loop makes into a book, so a fake book can finish on command without downloading anything. The loop itself is unchanged.