Skip to content

[AsyncStreaming] Add bidirectional adapters for the async writers#424

Merged
FranzBusch merged 2 commits into
apple:mainfrom
FranzBusch:fb-writer-adapters
Jun 2, 2026
Merged

[AsyncStreaming] Add bidirectional adapters for the async writers#424
FranzBusch merged 2 commits into
apple:mainfrom
FranzBusch:fb-writer-adapters

Conversation

@FranzBusch

Copy link
Copy Markdown
Member

Motivation

It is quite common to have one type of writer and wanting to adapt to the other type of writer.

Modifications

This PR adds two adapters into either direction between the writers to make conversion seamless.

Result

It is easy to go from one type of writer to the other type.

## Motivation

Real transports deliver structured data alongside end-of-stream and need to fuse the last write with the close. HTTP trailers and gRPC status are the obvious cases. Neither can be expressed by an empty-buffer terminator, and both lose H2/H3/QUIC's DATA+END_STREAM coalescing without a fused call.

## Modification

Adds `FinalElement: ~Copyable = Void` as a primary associated type on all four protocols. `AsyncReader` delivers it via a `consuming FinalElement?` closure parameter; `CallerAsyncReader` returns it. Both writers gain a consuming `finish` carrying the last chunk and the payload in one call.

`forEachBuffer`, `collect`, and the `pipe` variants thread the payload through. `collect` becomes consuming and gains a `Void`-final overload returning just the result.

Adds an "Alternatives considered" entry covering what would break without
this: HTTP body, gRPC, and fused close on H2/H3/QUIC.

## Result

The four protocols can back HTTP body, gRPC streaming, and similar shapes without giving up the fused close. Default `Void` keeps simple conformers unchanged; `Never` marks infinite streams. Custom and `~Copyable` payloads work end to end.
## Motivation

It is quite common to have one type of writer and wanting to adapt to the other type of writer.

## Modifications

This PR adds two adapters into either direction between the writers to make conversion seamless.

## Result

It is easy to go from one type of writer to the other type.
@FranzBusch FranzBusch force-pushed the fb-writer-adapters branch from b149503 to 97019b3 Compare May 26, 2026 14:06

@Catfish-Man Catfish-Man left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good :) nice functionality

/// closure-supplied buffer. The underlying writer's deferred-flush
/// behavior, if any, is preserved.
@available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, visionOS 2.0, *)
public struct AsyncWriterCallerAsyncWriterAdapter<

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[standard comment about names goes here]

we'll figure it out later

innerBuffer.append(element)
}
while innerBuffer.freeCapacity > 0 {
guard let element = consumer.next() else { return }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Getting all of this to lower to memcpy calls when the types are appropriate is going to be a fascinating exercise at some point. I never quite got it to for AsyncBytes

@FranzBusch FranzBusch marked this pull request as ready for review June 2, 2026 06:16
@FranzBusch FranzBusch merged commit 8e16632 into apple:main Jun 2, 2026
31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants