enum MainQueue
Swift
enum MainQueue
Work handed to the main thread from another one.
The reason this has to exist, rather than a worker simply writing @State
when it finishes: StateStorage is @Observable, and
withObservationTracking's onChange fires synchronously on whichever
thread performed the write. A background write would therefore run
ViewInvalidation.markBodyDirty on that thread — and ViewInvalidation,
FrameScheduler and FrameTasks are all single-threaded statics the run
loop reads every iteration. The race is real, not theoretical.
So the division is: a worker computes pure Sendable values and posts the
application of them here. async is the only member safe to call from
another thread, and everything it enqueues runs on the main thread.
AgentServer already had the shape of this — watcher thread, lock, wake
the loop, do the work on the main thread — but kept it private and
specific to its socket.
Sources/LavaUI/State.swift:461
Properties
static var hasPending: Bool { get }
Work posted before the loop installed its wake, or between drain and
the next pumpEvents. The loop must not park while this is true —
that is the switcher opening on an already-queued SubscribeWindows
snapshot and sitting on "no windows" until the pointer moves.
Sources/LavaUI/State.swift:502
Methods
static func async(_ work: @escaping @Sendable () -> Void)
Safe from any thread.
Sources/LavaUI/State.swift:476
static func drain()
Drained by LavaApp.run before invalidation is consumed, so a result
a worker delivered while the loop was parked lands in this frame
rather than the next one.
Sources/LavaUI/State.swift:490
static func install(wake: @escaping @Sendable () -> Void)
Installed once by LavaApp.run. Without it posts still arrive, but
cannot unblock a loop parked in pumpEvents — they would wait for
unrelated input, which for an idle window can be arbitrarily long.
Sources/LavaUI/State.swift:469