apple/swift-nio

ByteBuffer.withUnsafeReadableBytes does not accept async closures

Open

#2,630 建立於 2024年1月24日

在 GitHub 查看
 (5 留言) (0 反應) (0 負責人)Swift (8,453 star) (749 fork)batch import
good first issuekind/enhancement

描述

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?

貢獻者指南