aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGrégoire Duchêne <greg@awhk.org>2026-01-10 14:25:40 +0000
committerGrégoire Duchêne <greg@awhk.org>2026-01-17 11:06:16 +0000
commitb69b34ea14ec455c584c1dce736691379befe3d0 (patch)
tree33fff16e81a1692b6dbe81067336cc93a757be05
parentaa417238310ff54d6547f3f01a6a727c57bb044e (diff)
Make sleep inhibition more structuredHEADmain
-rw-r--r--.gitignore1
-rw-r--r--Sources/SleepInhibition.swift42
-rw-r--r--Sources/SleepInhibitor.swift48
-rw-r--r--Sources/Watcher.swift54
4 files changed, 60 insertions, 85 deletions
diff --git a/.gitignore b/.gitignore
index 24e5b0a..a915b32 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
.build
+caffeinate-downloads
diff --git a/Sources/SleepInhibition.swift b/Sources/SleepInhibition.swift
new file mode 100644
index 0000000..0d9a05e
--- /dev/null
+++ b/Sources/SleepInhibition.swift
@@ -0,0 +1,42 @@
+#if canImport(IOKit.pwr_mgt)
+import IOKit.pwr_mgt
+
+func withSleepInhibited(operation: () async throws -> Void) async throws {
+ let assertionID = try {
+ var assertionID = IOPMAssertionID(0)
+ let ioReturn = IOPMAssertionCreateWithDescription(
+ kIOPMAssertionTypePreventUserIdleSystemSleep as CFString,
+ "caffeinate-downloads" as CFString,
+ "There are files being downloaded" as CFString,
+ nil, nil, 0, nil,
+ &assertionID
+ )
+ guard ioReturn == kIOReturnSuccess else {
+ throw SleepInhibitionError("failed to inhibit sleep", ioReturn)
+ }
+ return assertionID
+ }()
+
+ do {
+ try await operation()
+ } catch is CancellationError {
+ }
+
+ let ioReturn = IOPMAssertionRelease(assertionID)
+ guard ioReturn == kIOReturnSuccess else {
+ throw SleepInhibitionError("failed to allow sleep", ioReturn)
+ }
+}
+
+struct SleepInhibitionError: CustomStringConvertible, Error {
+ let description: String
+
+ init(_ description: String, _ code: IOReturn) {
+ if let code = mach_error_string(code) {
+ self.description = "\(description): \(String(cString: code))"
+ } else {
+ self.description = "\(description): \(code)"
+ }
+ }
+}
+#endif
diff --git a/Sources/SleepInhibitor.swift b/Sources/SleepInhibitor.swift
deleted file mode 100644
index 0cfe895..0000000
--- a/Sources/SleepInhibitor.swift
+++ /dev/null
@@ -1,48 +0,0 @@
-import IOKit.pwr_mgt
-
-actor SleepInhibitor {
- var assertionID = IOPMAssertionID?.none
- var isInhibitingSleep: Bool { self.assertionID != nil }
-
- func create(name: String, details: String) throws {
- guard self.assertionID == nil else {
- return
- }
-
- var assertionID = IOPMAssertionID(0)
- let ioReturn = IOPMAssertionCreateWithDescription(
- kIOPMAssertionTypePreventUserIdleSystemSleep as CFString,
- name as CFString,
- details as CFString,
- nil, nil, 0, nil,
- &assertionID
- )
- guard ioReturn == kIOReturnSuccess else {
- throw SleepInhibitorError(ioReturn: ioReturn)
- }
- self.assertionID = assertionID
- }
-
- func release() throws {
- guard let assertionID = self.assertionID else {
- return
- }
-
- let ioReturn = IOPMAssertionRelease(assertionID)
- guard ioReturn == kIOReturnSuccess else {
- throw SleepInhibitorError(ioReturn: ioReturn)
- }
- self.assertionID = nil
- }
-}
-
-struct SleepInhibitorError: CustomStringConvertible, Error {
- let ioReturn: IOReturn
-
- var description: String {
- guard let cString = mach_error_string(self.ioReturn) else {
- return self.ioReturn.description
- }
- return String(cString: cString)
- }
-}
diff --git a/Sources/Watcher.swift b/Sources/Watcher.swift
index 3b08efd..75fefdf 100644
--- a/Sources/Watcher.swift
+++ b/Sources/Watcher.swift
@@ -7,49 +7,29 @@ struct Watcher: Service {
let logger: Logger
let suffix: String
- func run() async {
- let sleepInhibitor = SleepInhibitor()
+ func foundDownloads() async throws -> Bool {
+ try await FileSystem.shared.withDirectoryHandle(atPath: self.directory) {
+ try await $0.listContents().contains {
+ $0.name.string.hasSuffix(self.suffix)
+ }
+ }
+ }
+ func run() async throws {
while !Task.isCancelled {
- let isDownloading: Bool
do {
- isDownloading = try await FileSystem.shared.withDirectoryHandle(atPath: self.directory) {
- try await $0.listContents().contains {
- $0.name.string.hasSuffix(self.suffix)
- }
+ while try await !foundDownloads() {
+ try await Task.sleep(for: .seconds(30))
}
- } catch is CancellationError {
- return
- } catch {
- self.logger.error("Failed to check directory: \(error)")
- return
- }
- switch (isDownloading, await sleepInhibitor.isInhibitingSleep) {
- case (true, false):
- self.logger.debug("Ongoing downloads found, inhibiting sleep")
- do {
- try await sleepInhibitor.create(
- name: "caffeinate-downloads",
- details: "There are files being downloaded"
- )
- } catch {
- self.logger.error("Failed to create assertion: \(error)")
+ try await withSleepInhibited {
+ self.logger.debug("Ongoing downloads found, inhibiting sleep")
+ while try await foundDownloads() {
+ try await Task.sleep(for: .seconds(30))
+ }
+ self.logger.debug("No ongoing downloads found, allowing sleep")
}
-
- case (false, true):
- self.logger.debug("No ongoing downloads found, allowing sleep")
- do {
- try await sleepInhibitor.release()
- } catch {
- self.logger.error("Failed to release assertion: \(error)")
- }
-
- default:
- break
- }
-
- guard (try? await Task.sleep(for: .seconds(30))) != nil else {
+ } catch is CancellationError {
return
}
}