Skip to content

Commit 39eb2bd

Browse files
ZhaoChaoqunclaude
andcommitted
fix: cache running apps to avoid repeated DispatchQueue.main.sync
Call getRunningApps() once at the top of each resolve() entry point and pass the result through the entire call chain. This eliminates multiple DispatchQueue.main.sync dispatches per resolve invocation — findRunningBundleId, resolveViaTmuxClient, and resolveFromRunningApps now accept an apps parameter instead of fetching independently. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent bd0c163 commit 39eb2bd

1 file changed

Lines changed: 15 additions & 14 deletions

File tree

ClaudeIsland/Services/Window/TerminalResolver.swift

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ struct TerminalResolver: Sendable {
4242

4343
/// Resolve terminal using a pre-built process tree (avoids duplicate sysctl calls)
4444
nonisolated func resolve(claudePid: Int, tree: [Int: ProcessInfo]) -> ResolvedTerminal? {
45+
// Fetch running apps once for the entire resolve chain (single DispatchQueue.main.sync)
46+
let apps = Self.getRunningApps()
4547

4648
// Check if in tmux
4749
let isInTmux = ProcessTreeBuilder.shared.isInTmux(pid: claudePid, tree: tree)
@@ -52,7 +54,7 @@ struct TerminalResolver: Sendable {
5254
// Walk up process tree to find terminal app
5355
if let appInfo = TerminalAppRegistry.appInfo(forPid: claudePid, tree: tree) {
5456
// Verify the app is actually running and get its bundle ID
55-
if let bundleId = findRunningBundleId(for: appInfo) {
57+
if let bundleId = findRunningBundleId(for: appInfo, apps: apps) {
5658
return ResolvedTerminal(
5759
appInfo: appInfo,
5860
bundleId: bundleId,
@@ -65,24 +67,25 @@ struct TerminalResolver: Sendable {
6567
// If we're in tmux, the terminal might not be a direct parent.
6668
// Try to find it via tmux client PID.
6769
if isInTmux {
68-
return resolveViaTmuxClient(claudePid: claudePid, tree: tree, tty: tty)
70+
return resolveViaTmuxClient(claudePid: claudePid, tree: tree, tty: tty, apps: apps)
6971
}
7072

7173
// Last resort: check running applications against known terminals
72-
return resolveFromRunningApps(tty: tty)
74+
return resolveFromRunningApps(tty: tty, apps: apps)
7375
}
7476

7577
/// Resolve terminal using TTY (when PID is not available)
7678
nonisolated func resolve(tty: String) -> ResolvedTerminal? {
7779
// Parse TTY to find a PID associated with it
7880
let tree = ProcessTreeBuilder.shared.buildTree()
81+
let apps = Self.getRunningApps()
7982

8083
// Find processes on this TTY
8184
for (pid, info) in tree {
8285
guard let processTTY = info.tty else { continue }
8386
if tty.hasSuffix(processTTY) || processTTY == tty {
8487
// Found a process on this TTY, resolve from here
85-
if let resolved = resolveFromTree(pid: pid, tree: tree) {
88+
if let resolved = resolveFromTree(pid: pid, tree: tree, apps: apps) {
8689
return resolved
8790
}
8891
}
@@ -94,12 +97,12 @@ struct TerminalResolver: Sendable {
9497
// MARK: - Private
9598

9699
/// Walk up from a PID in a pre-built tree
97-
private nonisolated func resolveFromTree(pid: Int, tree: [Int: ProcessInfo]) -> ResolvedTerminal? {
100+
private nonisolated func resolveFromTree(pid: Int, tree: [Int: ProcessInfo], apps: [NSRunningApplication]) -> ResolvedTerminal? {
98101
let isInTmux = ProcessTreeBuilder.shared.isInTmux(pid: pid, tree: tree)
99102
let tty = findTTY(forPid: pid, tree: tree)
100103

101104
if let appInfo = TerminalAppRegistry.appInfo(forPid: pid, tree: tree),
102-
let bundleId = findRunningBundleId(for: appInfo) {
105+
let bundleId = findRunningBundleId(for: appInfo, apps: apps) {
103106
return ResolvedTerminal(
104107
appInfo: appInfo,
105108
bundleId: bundleId,
@@ -112,7 +115,7 @@ struct TerminalResolver: Sendable {
112115
}
113116

114117
/// Try to find the terminal via tmux client PID
115-
private nonisolated func resolveViaTmuxClient(claudePid: Int, tree: [Int: ProcessInfo], tty: String?) -> ResolvedTerminal? {
118+
private nonisolated func resolveViaTmuxClient(claudePid: Int, tree: [Int: ProcessInfo], tty: String?, apps: [NSRunningApplication]) -> ResolvedTerminal? {
116119
// Find tmux server in the parent chain
117120
var current = claudePid
118121
var depth = 0
@@ -139,7 +142,7 @@ struct TerminalResolver: Sendable {
139142

140143
// Walk up from this client to find the terminal
141144
if let appInfo = TerminalAppRegistry.appInfo(forPid: pid, tree: tree),
142-
let bundleId = findRunningBundleId(for: appInfo) {
145+
let bundleId = findRunningBundleId(for: appInfo, apps: apps) {
143146
return ResolvedTerminal(
144147
appInfo: appInfo,
145148
bundleId: bundleId,
@@ -155,11 +158,10 @@ struct TerminalResolver: Sendable {
155158
/// Fallback: check all running apps for known terminals.
156159
/// Only returns a result if exactly one terminal app is running,
157160
/// otherwise we can't determine which terminal owns this session.
158-
private nonisolated func resolveFromRunningApps(tty: String? = nil) -> ResolvedTerminal? {
159-
let runningApps = Self.getRunningApps()
161+
private nonisolated func resolveFromRunningApps(tty: String? = nil, apps: [NSRunningApplication]) -> ResolvedTerminal? {
160162
var matches: [(appInfo: TerminalAppInfo, bundleId: String)] = []
161163

162-
for app in runningApps {
164+
for app in apps {
163165
guard let bundleId = app.bundleIdentifier,
164166
let appInfo = TerminalAppRegistry.appInfo(forBundleId: bundleId) else {
165167
continue
@@ -181,10 +183,9 @@ struct TerminalResolver: Sendable {
181183
}
182184

183185
/// Find the bundle ID of a running instance for a terminal app
184-
private nonisolated func findRunningBundleId(for appInfo: TerminalAppInfo) -> String? {
185-
let runningApps = Self.getRunningApps()
186+
private nonisolated func findRunningBundleId(for appInfo: TerminalAppInfo, apps: [NSRunningApplication]) -> String? {
186187
for bundleId in appInfo.bundleIds {
187-
if runningApps.contains(where: { $0.bundleIdentifier == bundleId }) {
188+
if apps.contains(where: { $0.bundleIdentifier == bundleId }) {
188189
return bundleId
189190
}
190191
}

0 commit comments

Comments
 (0)