func write(_ span: Span<UInt8>) async {
let spanBufferPointer = span.withUnsafeBytes { $0 } // Get the pointer. Usually a bad pattern.
defer { extendLifetime(span) } // Pointer must stay valid until after the suspension point.
await withUnsafeContinuation { continuation in
// Pointer will be used in different thread until continuation is resumed.
scheduleWrite(spanBufferPointer, continuation)
}
}
Am I correct in thinking this is safe? Is there another solution?
The buffer pointer passed as an argument to body is valid only during the execution of withUnsafeBytes(_:). Do not store or return the pointer for later use.
I understand why it's documented that way. However, since the buffer pointer comes directly from the pointer stored in the span, I feel it should technically be fine as long as I keep the span alive. I thought it'd be good to double check though, because sometimes these unsafe constructs behave in a way I don't anticipate.
If there's a (documented) safe way to do this, I would happily use it, but I don't know of any.
It should be safe to use pointers from a read-only Span for as long as the span stays alive, so long as you don't violate the usual rules about the Span's memory (particularly that it can't be mutated while the Span is alive). You should be able to write this a bit more straightforwardly though by sinking the withUnsafeBytes into the continuation block:
func write(_ span: Span<UInt8>) async {
defer { extendLifetime(span) } // Pointer must stay valid until after the suspension point.
await withUnsafeContinuation { continuation in
span.withUnsafeBytes {
// Pointer will be used in different thread until continuation is resumed.
scheduleWrite($0, continuation)
}
}
}
(While not immediately relevant to your use case, in case someone finds this thread in a more general context: this is less true for MutableSpan, since its withUnsafe* accessors also assert exclusivity over the span while the pointer is in use. Since there can only be one active exclusive access to a memory location at a time, the MutableSpan itself would be unsafe to use while any pointers into it are potentially active.)
Good to know this should be safe, thank you! And indeed, that's a bit more straightforward. I wrote it the way I did because I thought it would be more clear that the pointer escapes.