Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,6 @@
## 2024-05-25 - Efficient Dictionary Initialization
**Learning:** Building a dictionary via a standard `for` loop by inserting elements one by one can cause unnecessary overhead due to repeated mutations. `reduce(into: [:])` is highly optimized in Swift to build collections without creating intermediate copies.
**Action:** Use `reduce(into: [:])` when constructing a dictionary from an array, particularly when uniqueness checks or transformations are required.
## 2024-05-24 - Parallelize bulk I/O operations
**Learning:** Using `TaskGroup` to parallelize `FileManager.removeItem` loops improves performance, but `Task.detached` does not escape the cooperative thread pool. Synchronous blocking calls must be wrapped in `withCheckedThrowingContinuation` and explicitly dispatched to a background GCD queue to avoid thread pool exhaustion.
**Action:** Always wrap synchronous POSIX/FileManager operations in `withCheckedThrowingContinuation` and dispatch them via GCD when parallelizing bulk I/O using Swift Concurrency.
42 changes: 38 additions & 4 deletions Sources/Cacheout/Cleaner/CacheCleaner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ actor CacheCleaner {
if moveToTrash {
try await trashDirectory(url)
} else {
try removeContents(of: url)
try await removeContents(of: url)
}
categoryFreed += result.sizeBytes
} catch {
Expand Down Expand Up @@ -133,12 +133,46 @@ actor CacheCleaner {
}
}

private func removeContents(of url: URL) throws {
/// ⚡ Bolt Optimization: Removes directory contents in parallel using a sliding-window TaskGroup.
/// Synchronous `removeItem` calls are explicitly dispatched to a GCD queue (`DispatchQueue.global`)
/// and wrapped in a Continuation. This prevents them from blocking threads in Swift's limited
/// cooperative thread pool, avoiding thread starvation during high-volume I/O.
private func removeContents(of url: URL) async throws {
let contents = try fileManager.contentsOfDirectory(
at: url, includingPropertiesForKeys: nil
)
for item in contents {
try fileManager.removeItem(at: item)
let currentFileManager = self.fileManager

let removeItemTask: @Sendable (URL) async throws -> Void = { item in
try await withCheckedThrowingContinuation { continuation in
DispatchQueue.global(qos: .userInitiated).async {
do {
try currentFileManager.removeItem(at: item)
continuation.resume(returning: ())
} catch {
continuation.resume(throwing: error)
}
}
}
}

try await withThrowingTaskGroup(of: Void.self) { group in
let maxConcurrency = 8
var index = 0

while index < min(maxConcurrency, contents.count) {
let item = contents[index]
group.addTask { try await removeItemTask(item) }
index += 1
}

while try await group.next() != nil {
if index < contents.count {
let item = contents[index]
group.addTask { try await removeItemTask(item) }
index += 1
}
}
}
}

Expand Down
Loading