Skip to content

Commit b431b66

Browse files
committed
Fix workspace creation crash after restore
1 parent ae06480 commit b431b66

3 files changed

Lines changed: 27 additions & 12 deletions

File tree

Sources/PortScanner.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ import Foundation
1515
final class PortScanner: @unchecked Sendable {
1616
static let shared = PortScanner()
1717

18-
/// Callback delivers `(workspaceId, panelId, ports)` on main thread.
19-
var onPortsUpdated: ((_ workspaceId: UUID, _ panelId: UUID, _ ports: [Int]) -> Void)?
18+
/// Callback delivers `(workspaceId, panelId, ports)` on the main actor.
19+
var onPortsUpdated: (@MainActor (_ workspaceId: UUID, _ panelId: UUID, _ ports: [Int]) -> Void)?
2020

2121
// MARK: - State (all guarded by `queue`)
2222

@@ -171,7 +171,7 @@ final class PortScanner: @unchecked Sendable {
171171

172172
private func deliverResults(_ results: [(PanelKey, [Int])]) {
173173
guard let callback = onPortsUpdated else { return }
174-
DispatchQueue.main.async {
174+
Task { @MainActor in
175175
for (key, ports) in results {
176176
callback(key.workspaceId, key.panelId, ports)
177177
}

Sources/TabManager.swift

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5034,8 +5034,19 @@ extension TabManager {
50345034
)
50355035
}
50365036

5037+
private func releaseRestoredAwayWorkspace(_ workspace: Workspace) {
5038+
// Session restore replaces the bootstrap workspace objects with freshly
5039+
// restored ones. Tear the old graph down after the atomic swap so late
5040+
// panel/socket callbacks cannot keep mutating hidden pre-restore state.
5041+
AppDelegate.shared?.notificationStore?.clearNotifications(forTabId: workspace.id)
5042+
workspace.teardownAllPanels()
5043+
workspace.teardownRemoteConnection()
5044+
workspace.owningTabManager = nil
5045+
}
5046+
50375047
func restoreSessionSnapshot(_ snapshot: SessionTabManagerSnapshot) {
5038-
for tab in tabs {
5048+
let previousTabs = tabs
5049+
for tab in previousTabs {
50395050
unwireClosedBrowserTracking(for: tab)
50405051
}
50415052
let existingProbeKeys = Set(workspaceGitProbeGenerationByKey.keys)
@@ -5099,6 +5110,12 @@ extension TabManager {
50995110
// never see an intermediate state with empty tabs or nil selection.
51005111
tabs = newTabs
51015112
selectedTabId = newSelectedId
5113+
let existingIds = Set(newTabs.map(\.id))
5114+
pruneBackgroundWorkspaceLoads(existingIds: existingIds)
5115+
sidebarSelectedWorkspaceIds.formIntersection(existingIds)
5116+
for workspace in previousTabs {
5117+
releaseRestoredAwayWorkspace(workspace)
5118+
}
51025119
for workspace in newTabs {
51035120
let terminalPanels = workspace.panels.values.compactMap { $0 as? TerminalPanel }
51045121
for terminalPanel in terminalPanels {

Sources/TerminalController.swift

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,14 +1020,12 @@ class TerminalController {
10201020

10211021
// Wire batched port scanner results back to workspace state.
10221022
PortScanner.shared.onPortsUpdated = { [weak self] workspaceId, panelId, ports in
1023-
MainActor.assumeIsolated {
1024-
guard let self, let tabManager = self.tabManager else { return }
1025-
guard let workspace = tabManager.tabs.first(where: { $0.id == workspaceId }) else { return }
1026-
let validSurfaceIds = Set(workspace.panels.keys)
1027-
guard validSurfaceIds.contains(panelId) else { return }
1028-
workspace.surfaceListeningPorts[panelId] = ports.isEmpty ? nil : ports
1029-
workspace.recomputeListeningPorts()
1030-
}
1023+
guard let self, let tabManager = self.tabManager else { return }
1024+
guard let workspace = tabManager.tabs.first(where: { $0.id == workspaceId }) else { return }
1025+
let validSurfaceIds = Set(workspace.panels.keys)
1026+
guard validSurfaceIds.contains(panelId) else { return }
1027+
workspace.surfaceListeningPorts[panelId] = ports.isEmpty ? nil : ports
1028+
workspace.recomputeListeningPorts()
10311029
}
10321030

10331031
// Accept connections in background thread

0 commit comments

Comments
 (0)