Использование таймера Swift в инструменте / демоне командной строки MacOS, который использует Swift-NIO для работы в сети: RunLoop vs wait issue

Я конвертирую инструмент / демон командной строки Swift MacOS для использования Swift-NIO для работы в сети. Это мой первый проект Swift-NIO.

Инструмент запускает таймер каждые 0,1 секунды. Вот строка внизу main.swift, которая запускает демон / цикл выполнения до преобразования Swift-NIO:

RunLoop.current.run()

Вот таймер в моем классе Universe.swift init(). Всегда существует ровно один экземпляр этого класса:

    timer = Timer(timeInterval: 1.0 / updatesPerSecond, target: self, selector: #selector(timerFired), userInfo: nil, repeats: true)
    timer?.tolerance = 0.3 / updatesPerSecond
    debugPrint("Timer initialized")
    if let timer = timer {
        RunLoop.current.add(timer, forMode: RunLoop.Mode.common)
    }

В этой конфигурации таймер срабатывает 10 раз в секунду, как и ожидалось. Но если я получаю какой-либо сетевой ввод, моя библиотека Swift-NIO вылетает, потому что она не находится в ожидаемом цикле событий.

В Swift-NIO я должен добавить строку channel.closeFuture.wait() в конец моего main.swift:

// This will never unblock as we don't close the ServerChannel.
try channel.closeFuture.wait()
RunLoop.current.run()

Это решает проблему сбоя Swift-NIO, но тогда я никогда не добираюсь до RunLoop таймера, поэтому мой таймер не срабатывает.

Как я могу использовать Swift-NIO для получения (и отправки) сетевых данных при работающем таймере?

Если это поможет, полный открытый исходный код этого проекта находится по адресу https://github.com/darrellroot/netrek-server-swift.

1 ответ

Лукаса был прав. Я упустил (и не понял) какой-то важный контекст.

Мой таймер в конечном итоге пытался отправить данные с помощью SwiftNIO следующим образом:

    if let context = context {
        let buffer = context.channel.allocator.buffer(bytes: data)
        _ = context.channel.writeAndFlush(buffer)
    }

Исправление заключалось в "диспетчеризации" отправки трафика в связанный с контекстом EventLoop:

    if let context = context {
        context.eventLoop.execute {
                let buffer = context.channel.allocator.buffer(bytes: data)
                _ = context.channel.writeAndFlush(buffer)
        }
    }

Он очень похож на DispatchQueue.main.async { } для обновления графического интерфейса в приложениях iOS, но с другой терминологией, связанной с Swift-NIO.

Другие вопросы по тегам