| index 9d8df8c..4c5f8c6 100644 |
| --- a/ios/LChatCore/Sources/LChatCore/CoalescedRunner.swift |
| +++ b/ios/LChatCore/Sources/LChatCore/CoalescedRunner.swift |
| @@ -1,46 +1,5 @@ |
| -import Foundation |
| +import RastrilloNative |
| |
| -// 🤖 Serialize fire-and-forget async work with trailing-edge coalescing: |
| -// while one pass runs, any number of further run() calls collapse into ONE |
| -// rerun after it finishes (using the running call's closure — callers all |
| -// request the same work). Built for ThreadStore.refresh, which refires on |
| -// every socket frame and from several UI paths while a full pass takes a |
| -// second-plus on a big space: uncoalesced, overlapping passes raced |
| -// duplicate /api/threads fetches and stacked their folds' publishes into |
| -// the 120–270ms frame hitches the 2026-07-20 device trace pinned on |
| -// NativeChatScreen's re-render. |
| -// |
| -// A caller that arrives mid-pass AWAITS the trailing rerun rather than |
| -// returning early: callers like createChat read the published state right |
| -// after run() and rely on a pass that STARTED after their call (the new |
| -// thread must be in `threads` when run() returns). The early-return first |
| -// cut broke that quietly — repaint callers didn't care, createChat did. |
| -@MainActor |
| -public final class CoalescedRunner { |
| - private var current: Task<Void, Never>? |
| - private var queued = false |
| - |
| - public init() {} |
| - |
| - public func run(_ pass: @escaping @MainActor () async -> Void) async { |
| - if let current { |
| - // Mid-pass: the trailing rerun starts after this call, so |
| - // awaiting the whole task is the freshness guarantee. |
| - queued = true |
| - await current.value |
| - return |
| - } |
| - let task = Task { @MainActor [weak self] in |
| - repeat { |
| - self?.queued = false |
| - await pass() |
| - } while self?.queued == true |
| - // Cleared as the task's LAST act, with no suspension after the |
| - // loop — so no caller can ever observe a finished-but-current |
| - // task and return without its covering pass. |
| - self?.current = nil |
| - } |
| - current = task |
| - await task.value |
| - } |
| -} |
| +// Preserve the app core API while sharing the refresh and awaiter-freshness |
| +// contract with the other native clients. Keep the app regression tests. |
| +public typealias CoalescedRunner = RastrilloNative.CoalescedRunner |