【问题标题】:Not connecting to RPC server in release mode, but works fine in debug mode在发布模式下无法连接到 RPC 服务器,但在调试模式下工作正常
【发布时间】:2020-05-07 11:21:59
【问题描述】:

我有一个命令行应用程序,它执行以下操作:

  • 下载带有种子链接的 RSS 源
  • 将其存储在 sqlite 数据库中并将它们标记为“已添加”或“已忽略”
  • 连接到传输服务器(在我的本地网络中)
  • 从标记为“已添加”的 sqlite 加载项目并添加到传输服务器

以上在调试模式下工作正常。但是,当我为发布构建并尝试直接运行或从 launchd 运行时,它总是超时。最相关的代码在下面的main.swift 中。

private func getTransmissionClient() -> Transmission? {
        let client = Transmission(
            baseURL: serverConfig.server,
            username: serverConfig.username,
            password: serverConfig.password)

        var cancellables = Set<AnyCancellable>()

        let group = DispatchGroup()
        group.enter()
        print("[INFO] Connecting to client")
        client.request(.rpcVersion)
            .sink(
                receiveCompletion: { _ in group.leave() },
                receiveValue: { rpcVersion in
                    print("[INFO]: Successfully Connected! RPC Version: \(rpcVersion)")
            })
            .store(in: &cancellables)
        let wallTimeout = DispatchWallTime.now() +
            DispatchTimeInterval.seconds(serverConfig.secondsTimeout ?? 15)
        let res = group.wait(wallTimeout: wallTimeout)

        if res == DispatchTimeoutResult.success {
            return client
        } else {
            return nil
        }

    }

    public func updateTransmission() throws {

        print("[INFO] [\(Date())] Starting Transmission Update")

        let clientOpt = getTransmissionClient()
        guard let client = clientOpt else {
            print("[ERROR] Failed to connect to transmission client")
            exit(1)
        }

        var cancellables = Set<AnyCancellable>()

        let items = try store.getPendingDownload()
        print("[INFO] [\(Date())] Adding \(items.count) new items to transmission")

        let group = DispatchGroup()
        for item in items {

            let linkComponents = "\(item.link)".components(separatedBy: "&")
            assert(linkComponents.count > 0, "Link seems wrong")

            group.enter()
            client.request(.add(url: item.link))
                .sink(receiveCompletion: { completion in
                    if case let .failure(error) = completion {
                        print("[Failure] \(item.title)")
                        print("[Failure] Details: \(error)")

                    }
                    group.leave()
                }, receiveValue: { _ in
                    print("[Success] \(item.title)")
                    do {
                        try self.store.update(item: item, with: .downloaded)

                    } catch {
                        print("[Error] Couldn't save new status to DB")
                    }
                })
                .store(in: &cancellables)
        }


        let wallTimeout = DispatchWallTime.now() +
            DispatchTimeInterval.seconds(serverConfig.secondsTimeout ?? 15)
        let res = group.wait(wallTimeout: wallTimeout)
        if res == DispatchTimeoutResult.success {
            print("Tasks successfully submitted")
        } else {
            print("Timed out")
            exit(1)
        }
    }

奇怪的是,在我添加数据库之前,代码似乎运行良好。 DispatchGroup 以及 Transmission-Swift 客户端已经存在。我猜我所做的事情是被编译器“优化掉”了?这只是猜测,虽然在 StackOverflow 上看到了其他一些问题,但我仍然不清楚。

我使用的是 macOS 10.15 和 Swift 5.2.2。

github 中提供了完整代码(链接到存在错误的特定提交)

【问题讨论】:

    标签: swift rpc combine transmission-daemon


    【解决方案1】:

    我在https://forums.swift.org/t/not-connecting-to-rpc-server-in-release-mode-but-works-fine-in-debug-mode/36251 向 Swift 论坛寻求帮助,这里是它的要点:

    • 调试与发布错误在 Apple 生态系统中很常见。
    • 出现上述情况的一个常见原因是:编译器在发布模式下具有更激进的保留和释放模式。
    • 我的问题恰恰是:某个类比它应该被更早地处理掉了,它正是订阅的可取消对象,所以我的服务器请求在中间被取消了。

    这个commit 修复了它,它基本上做了以下事情:

    diff --git a/Sources/TorrentRSS/TorrentRSS.swift b/Sources/TorrentRSS/TorrentRSS.swift
    index 17e1a6b..0b80cd5 100644
    --- a/Sources/TorrentRSS/TorrentRSS.swift
    +++ b/Sources/TorrentRSS/TorrentRSS.swift
    @@ -63,6 +63,10 @@ public struct TorrentRSS {
                 DispatchTimeInterval.seconds(serverConfig.secondsTimeout ?? 15)
             let res = group.wait(wallTimeout: wallTimeout)
    
    +        for cancellable in cancellables {
    +            cancellable.cancel()
    +        }
    +
             if res == DispatchTimeoutResult.success {
                 return client
             } else {
    @@ -117,6 +121,11 @@ public struct TorrentRSS {
             let wallTimeout = DispatchWallTime.now() +
                 DispatchTimeInterval.seconds(serverConfig.secondsTimeout ?? 15)
             let res = group.wait(wallTimeout: wallTimeout)
    +
    +        for cancellable in cancellables {
    +            cancellable.cancel()
    +        }
    +
             if res == DispatchTimeoutResult.success {
                 print("Tasks successfully submitted")
             } else {
    

    显式调用cancellable 可以避免对象在它应该被释放之前被释放。那个特定的位置是我打算处理对象的地方,而不是更早。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多