Skip to content

Remove Sendable from AsyncBufferedByteIterator #220

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 20, 2022
Merged
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
29 changes: 7 additions & 22 deletions Sources/AsyncAlgorithms/AsyncBufferedByteIterator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
/// }
///
///
public struct AsyncBufferedByteIterator: AsyncIteratorProtocol, Sendable {
public struct AsyncBufferedByteIterator: AsyncIteratorProtocol {
public typealias Element = UInt8
@usableFromInline var buffer: _AsyncBytesBuffer

Expand All @@ -64,10 +64,13 @@ public struct AsyncBufferedByteIterator: AsyncIteratorProtocol, Sendable {
}
}

@available(*, unavailable)
extension AsyncBufferedByteIterator: Sendable { }

@frozen @usableFromInline
internal struct _AsyncBytesBuffer: @unchecked Sendable {
internal struct _AsyncBytesBuffer {
@usableFromInline
final class Storage: Sendable {
final class Storage {
fileprivate let buffer: UnsafeMutableRawBufferPointer

init(
Expand All @@ -85,7 +88,7 @@ internal struct _AsyncBytesBuffer: @unchecked Sendable {
}
}

@usableFromInline internal var storage: Storage
@usableFromInline internal let storage: Storage
@usableFromInline internal var nextPointer: UnsafeRawPointer
@usableFromInline internal var endPointer: UnsafeRawPointer

Expand All @@ -110,24 +113,6 @@ internal struct _AsyncBytesBuffer: @unchecked Sendable {
}
try Task.checkCancellation()
do {
// If two tasks have access to this iterator then the references on
// the storage will be non uniquely owned. This means that any reload
// must happen into its own fresh buffer. The consumption of those
// bytes between two tasks are inherently defined as potential
// duplication by the nature of sending that buffer across the two
// tasks - this means that the brief period in which they may be
// sharing non reloaded bytes is to be expected; basically in that
// edge case of making the iterator and sending that across to two
// places to iterate is asking for something bizzare and the answer
// should not be crash, but it definitely cannot be consistent.
//
// The unique ref check is here to prevent the potentials of a crashing
// scenario.
if !isKnownUniquelyReferenced(&storage) {
// The count is not mutated across invocations so the access is safe.
let capacity = storage.buffer.count
storage = Storage(capacity: capacity)
}
let readSize: Int = try await readFunction(storage.buffer)
if readSize == 0 {
finished = true
Expand Down