Skip to content

Commit 6405bb2

Browse files
fix(macos): hold the sleep assertion for any non-seeding torrent
The gate was `status == "Downloading"`, which is narrower than the intent. The engine derives that status from the piece currently being fetched (`Peer.status()` is `nil | :seed | :connecting_to_peers | index`), so an **incomplete** torrent reports `"Idle"` or `"Connecting"` whenever no piece is assigned to it. Behind CGNAT, where a torrent runs on one to three peers, that is a state it passes through constantly while hunting for somewhere to ask — and letting the Mac sleep there strands it exactly when re-dialling is the only thing that can rescue it, because sleep drops every peer connection it might have been about to establish. `!= "Seeding"` is the same test the Dock menu already applies when it files torrents under its "Downloading:" heading, so the assertion and the menu now agree on what counts as active. There is no paused or stopped status to worry about letting through: `Peer.status()` has no such value, and a stopped torrent is not in the list at all. Found by reading `powerd`'s own log after deploying the original commit — `log show --predicate 'eventMessage CONTAINS "ElixirTorrentWebUI"'` shows every acquire and release with an age, which is a better instrument for this than `pmset -g assertions` because it shows the transitions rather than the current state. It recorded a release-and-reacquire on a torrent that never stopped being incomplete. Forty consecutive two-second samples afterwards all read `"Downloading"`, so this specific flap is not reproduced on demand — but the code path is plainly there, and the conservative gate has no upside on AC power. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent db453dd commit 6405bb2

1 file changed

Lines changed: 11 additions & 3 deletions

File tree

priv/macos/src/SleepPreventer.swift

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,22 @@ final class SleepPreventer {
2424
private var assertionID: IOPMAssertionID = 0
2525
private var isHeld = false
2626

27+
/// Anything that is not seeding still needs the machine awake, which is why
28+
/// this is `!= "Seeding"` rather than `== "Downloading"`. The engine derives
29+
/// the status from the piece currently being fetched, so an incomplete
30+
/// torrent reports `"Connecting"` or `"Idle"` whenever no piece is assigned
31+
/// — and behind CGNAT, where a torrent runs on one to three peers, that is a
32+
/// state it passes through constantly while hunting for somewhere to ask.
33+
/// Letting the Mac sleep there would strand it exactly when re-dialling is
34+
/// the only thing that can rescue it. This also matches the Dock menu, which
35+
/// files every non-seeding torrent under "Downloading:".
36+
///
2737
/// `downKbps` cannot be the trigger even though it reads like the natural
2838
/// one: the API reports `0.0` for torrents that are demonstrably
2939
/// progressing, because the underlying counter is piece-granular and the
3040
/// sample window is shorter than one piece (engine `PLAN.md` open bug #53b).
31-
/// Gating on it would release the assertion exactly when a slow torrent
32-
/// needs the machine awake most. `status` is authoritative.
3341
private static func hasIncompleteTorrent(_ torrents: [DockTorrent]) -> Bool {
34-
torrents.contains { $0.status == "Downloading" }
42+
torrents.contains { $0.status != "Seeding" }
3543
}
3644

3745
/// A machine with no battery (desktop) reports AC, which is what we want.

0 commit comments

Comments
 (0)