Sorry, the question about async networking is now becoming a question about task cancellation. Feel free to throw me to new thread (pun intended).
The bulk of the Swift concurrency runtime is in the Swift open source.
I've got the swift repo and do look at it but not to that depth. Maybe one-day when I have lots of time :)
I strongly recommend that you watch WWDC 2021 Session 10134 Explore structured concurrency in Swift.
I did this, and learned a lot but not what I expected. I fully understand while a Task needs to complete and be able to be cancelled. What I fail to see, is how to opt-in.
To try and explain, below is a simple example that works (assuming ostrich algorithm). Should the withCheckedContinuation be replaced with withTaskCancellationHandler? [1] Even if it is, how would I cancel a connection that is waiting for data to be received?
Maybe I am starting to understand just enough to realise that something needs to change in the networking layer?
[1] I found very little on withTaskCancellationHandler in documentation and online which may explain why it is not clear to me.
import Foundation
import Network
class Udp {
private var listner: NWListener
private var receiveCallback: ((Data, NWEndpoint) -> Void)?
func receive() async -> (Data, NWEndpoint) {
await withCheckedContinuation { continuation in
asyncReceive { data, endpoint in
continuation.resume(returning: (data, endpoint))
}
}
}
private func asyncReceive(callback: @escaping (Data, NWEndpoint) -> Void) {
receiveCallback = callback
}
init(localPort: UInt16) throws {
listner = try NWListener(using: .udp, on: NWEndpoint.Port(rawValue: localPort)!)
listner.newConnectionHandler = { connection in
connection.start(queue: .global())
self.receive(from: connection)
}
listner.start(queue: .global())
}
func receive(from connection: NWConnection) {
connection.receiveMessage { content, context, isComplete, error in
if let rxData = content {
if let receiveCallback = self.receiveCallback {
receiveCallback(rxData, connection.endpoint)
}
}
// Receive again:
self.receive(from: connection)
}
}
}
print("This app listens for UDP on port 7000.")
print("Use:")
print(" nc -u 127.0.0.1 7000")
print("to start sending data to me. I'll let you know when I've received it.")
let udp = try! Udp(localPort: 7000)
let (data, sender) = await udp.receive()
print("Received data from \(sender)")