Skip to content
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

ByteBuffer.withUnsafeReadableBytes does not accept async closures #2630

Open
blindspotbounty opened this issue Jan 24, 2024 · 1 comment
Open
Labels
enhancement New feature or request good first issue Good for newcomers

Comments

@blindspotbounty
Copy link

blindspotbounty commented Jan 24, 2024

I haven't found a template for feature request, improvement or question, thus adding it as blank one.

Recently, there is NIOAsyncChannel was added to swift-nio. It is very convenient to use it with swift concurrency.
However, sometimes it is hard to dial with ByteBuffer without async interface, such as ByteBuffer.withUnsafeReadableBytes that accepts async closure.

I have the following use case:

let channel = try await ClientBootstrap(group: Self.eventLoopGroup)
        .connect(host: ipAddress, port: port) { channel in
            channel.eventLoop.makeCompletedFuture {
                try channel.pipeline.syncOperations.addHandler(ByteToMessageHandler(StreamDecoder(self.logger)))
                
                return try NIOAsyncChannel(
                    wrappingChannelSynchronously: channel,
                    configuration: .init(
                        backPressureStrategy: .init(lowWatermark: 5, highWatermark: 10),
                        inboundType: ByteBuffer.self))
            }
        }
        try await channel.executeThenClose { inbound in
            for try await buffer in inbound {
                try await buffer.withUnsafeReadableBytes { ptr in // cannot use it here because withUnsafeReadableBytes does not support swift concurrency
                     header = ProtocolHeader(ptr) // some protocol that accepts UnsafeRawBufferPointer
                     try await hander.handleMessage(header)
                }
            }
        }

Thus I have to copy memory from ByteBuffer which I believe is not efficient:

        try await channel.executeThenClose { inbound in
            for try await buffer in inbound {
                      let buf = buffer.withUnsafeReadableBytes { buf in
                          // Workaround, no async method in ByteBuffer...
                          let newBuf = UnsafeMutableRawBufferPointer.allocate(byteCount: buf.count, alignment: 8)
                          newBuf.copyBytes(from: buf)
                          return newBuf
                      }
                      defer {
                          buf.deallocate()
                      }
                     header = ProtocolHeader(UnsafeRawBufferPointer(buf))
                     try await hander.handleMessage(header)
            }
        }

I am looking for a good solution here. The easiest way from my point of view seems to allow async withUnsafeReadableBytes.
However, it might be not inline with its design, therefore I am looking for a good way to solve the issue.
Could you advise on above, please?

@Lukasa
Copy link
Contributor

Lukasa commented Jan 24, 2024

In general this isn't an unreasonable request, though we'll need to be a little careful to make sure that the CoW stuff works properly.

@Lukasa Lukasa added enhancement New feature or request good first issue Good for newcomers labels Jan 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers
Projects
None yet
Development

No branches or pull requests

2 participants