swift 6 并发错误“在同步非隔离上下文中调用参与者隔离实例方法‘getResponse(for:)’”

问题描述 投票:0回答:1

我正在尝试使用 NIO 创建一个 MockHTTPServer。试图使其兼容 swift 6。我一直在试图弄清楚如何解决 MockHTTPHandler 中发生的

Call to actor-isolated instance method 'getResponse(for:)' in a synchronous nonisolated context
问题。我希望有人能告诉我这很简单。将有问题的代码包装在任务中会产生不同的错误:“‘() async -> ()’类型的任务隔离值作为强传输参数传递;以后的访问可能会发生争用”,我无法弄清楚哪个函数调用正在比赛。

fileprivate actor MockHTTPServer {
    private let group: EventLoopGroup
    private let channel: Channel
    let port: Int
    
    private var responses: [String: (HTTPResponseStatus, String)] = [:]
    
    private init(group: EventLoopGroup, channel: Channel) {
        self.group = group
        self.channel = channel
        self.port = channel.localAddress?.port ?? -1
    }
    
    static func start(group: EventLoopGroup) async throws -> MockHTTPServer {
        let server = try await withCheckedThrowingContinuation { continuation in
            let bootstrap = ServerBootstrap(group: group)
                .serverChannelOption(ChannelOptions.backlog, value: 256)
                .serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
                .childChannelInitializer { channel in
                    channel.pipeline.configureHTTPServerPipeline().flatMap { _ in
                        let handler = MockHTTPHandler()
                        return channel.pipeline.addHandler(handler)
                    }
                }
            
            bootstrap.bind(host: "localhost", port: 0).whenComplete { result in
                switch result {
                case .success(let channel):
                    let server = MockHTTPServer(group: group, channel: channel)
                    channel.pipeline.handler(type: MockHTTPHandler.self).whenSuccess { handler in
                        handler.server = server
                    }
                    continuation.resume(returning: server)
                case .failure(let error):
                    continuation.resume(throwing: error)
                }
            }
        }
        
        return server
    }
    
    func stop() async throws {
        try await channel.close()
    }
    
    func addResponse(for path: String, statusCode: HTTPResponseStatus, body: String) {
        responses[path] = (statusCode, body)
    }
    
    func getResponse(for path: String) -> (HTTPResponseStatus, String)? {
        return responses[path]
    }
    
    private final class MockHTTPHandler: ChannelInboundHandler {
        typealias InboundIn = HTTPServerRequestPart
        typealias OutboundOut = HTTPServerResponsePart
        
        weak var server: MockHTTPServer?
        
        func channelRead(context: ChannelHandlerContext, data: NIOAny) {
            let reqPart = unwrapInboundIn(data)
            
            guard case .head(let head) = reqPart else { return }
            
            guard let server = self.server else {
                respond(context: context, status: .internalServerError, body: "Server not found")
                return
            }
            
                if let (status, body) = server.getResponse(for: head.uri) {
                    respond(context: context, status: status, body: body)
                } else {
                    respond(context: context, status: .notFound, body: "Not Found")
                }
        }
        
        private func respond(context: ChannelHandlerContext, status: HTTPResponseStatus, body: String) {
            var headers = HTTPHeaders()
            headers.add(name: "Content-Type", value: "application/json")
            headers.add(name: "Content-Length", value: "\(body.utf8.count)")
            
            let head = HTTPResponseHead(version: .http1_1, status: status, headers: headers)
            context.write(wrapOutboundOut(.head(head)), promise: nil)
            
            var buffer = context.channel.allocator.buffer(capacity: body.utf8.count)
            buffer.writeString(body)
            context.write(wrapOutboundOut(.body(.byteBuffer(buffer))), promise: nil)
            context.writeAndFlush(wrapOutboundOut(.end(nil)), promise: nil)
        }
    }
}
swift concurrency
1个回答
0
投票

您可以在这个优秀的博客中阅读有关此错误的更多信息。在您的示例中,编译器会引发有关属性

weak var server: MockHTTPServer?
的错误,因为它是可变的,因为
var
关键字可以引入
data races

© www.soinside.com 2019 - 2024. All rights reserved.