This is a question about async/await that has been on my mind ever since it was mentioned, but that I haven't found an answer for: Does the async system automatically know when my code is blocked and return control to another task?
I'm trying to implement a breadth-first filesystem enumerator as an AsyncSequence. When I block on reading a directory, will control automatically be transferred to another task, or should I be using non-blocking reads and calling Task.yield() myself?
Here's my code sample:
mutating func next() async throws -> String? {
while true {
if self.entries.count == 0 && self.directoryQueue.count == 0 {
return nil
}
if self.entries.count > 0 {
return self.entries.removeFirst()
}
let currentDirectory = directoryQueue.removeFirst()
guard let unixDirPtr = opendir(currentDirectory) else {
throw HXErrors.general("Could not open directory \(currentDirectory)")
}
defer {
closedir(unixDirPtr)
}
var dirListing = [String]()
var directoriesInListing = [String]()
while let entry = readdir(unixDirPtr) {
let name = withUnsafePointer(to:entry.pointee.d_name) {
$0.withMemoryRebound(to:UInt8.self, capacity:Int(entry.pointee.d_namlen)) {
String(cString:$0)
}
}
if name == "." || name == ".." {
continue
}
dirListing.append(name)
if entry.pointee.d_type == DT_DIR {
directoriesInListing.append(name)
}
}
self.entries.append(
contentsOf:dirListing.lazy
.sorted {$0.localizedCompare($1) == .orderedAscending}
.map {currentDirectory + "/" + $0}
)
let prune = self.prune // need to assign to a local variable so we don't capture self
self.directoryQueue.append(
contentsOf:directoriesInListing.lazy
.sorted {$0.localizedCompare($1) == .orderedAscending}
.map {currentDirectory + "/" + $0}
.filter {!prune($0)}
)
}
}
Thanks in advance for any enlightenment.