fix(app): close wake recovery timeout gaps

This commit is contained in:
Brennan Benson
2026-07-22 13:30:33 -07:00
parent b0e86c79a5
commit 4fc1ec709d
11 changed files with 357 additions and 49 deletions
+9 -4
View File
@@ -2487,11 +2487,16 @@ app.on('will-quit', (e) => {
// Why: a wedged transport (half-open post-sleep socket) can leave one
// member unsettled forever and block app.quit() until Force Quit (#9447).
settleTeardownWithinDeadline([
daemonTeardown,
rpcStopAndClear,
watcherShutdown,
emulatorShutdown
{ name: 'daemon', promise: daemonTeardown },
{ name: 'runtime-rpc', promise: rpcStopAndClear },
{ name: 'watchers', promise: watcherShutdown },
{ name: 'emulator', promise: emulatorShutdown }
])
.then((pendingTeardowns) => {
if (pendingTeardowns.length > 0) {
console.warn('[shutdown] Quit teardown deadline reached', { pendingTeardowns })
}
})
.then(() => shutdownTelemetry())
.then(() => shutdownObservability())
.catch(() => {
+12 -7
View File
@@ -13,27 +13,32 @@ describe('settleTeardownWithinDeadline', () => {
vi.useFakeTimers()
let resolved = false
const pending = settleTeardownWithinDeadline([
Promise.resolve(),
Promise.reject(new Error('daemon disconnect failed'))
{ name: 'daemon', promise: Promise.resolve() },
{ name: 'runtime-rpc', promise: Promise.reject(new Error('daemon disconnect failed')) }
]).then(() => {
resolved = true
})
await vi.advanceTimersByTimeAsync(0)
await pending
expect(resolved).toBe(true)
expect(vi.getTimerCount()).toBe(0)
})
it('resolves at the deadline when a teardown never settles', async () => {
it('reports the teardowns still pending at the deadline', async () => {
vi.useFakeTimers()
const pending = settleTeardownWithinDeadline([
{ name: 'daemon', promise: Promise.resolve() },
{ name: 'runtime-rpc', promise: new Promise(() => {}) }
])
await vi.advanceTimersByTimeAsync(WILL_QUIT_TEARDOWN_DEADLINE_MS - 1)
let resolved = false
const pending = settleTeardownWithinDeadline([new Promise(() => {})]).then(() => {
void pending.then(() => {
resolved = true
})
await vi.advanceTimersByTimeAsync(WILL_QUIT_TEARDOWN_DEADLINE_MS - 1)
expect(resolved).toBe(false)
await vi.advanceTimersByTimeAsync(1)
await pending
expect(resolved).toBe(true)
await expect(pending).resolves.toEqual(['runtime-rpc'])
expect(vi.getTimerCount()).toBe(0)
})
// Why: pin the magnitude so the wedge escape hatch cannot be silently
+23 -9
View File
@@ -7,15 +7,29 @@
// enough that a wedged teardown never needs Force Quit.
export const WILL_QUIT_TEARDOWN_DEADLINE_MS = 20_000
export function settleTeardownWithinDeadline(
teardowns: Promise<unknown>[],
export type NamedQuitTeardown = {
name: string
promise: Promise<unknown>
}
export async function settleTeardownWithinDeadline(
teardowns: readonly NamedQuitTeardown[],
deadlineMs: number = WILL_QUIT_TEARDOWN_DEADLINE_MS
): Promise<void> {
const deadline = new Promise<void>((resolve) => {
const timer = setTimeout(resolve, deadlineMs)
if (typeof timer.unref === 'function') {
timer.unref()
}
): Promise<string[]> {
const pendingNames = new Set(teardowns.map(({ name }) => name))
const settled = Promise.allSettled(
teardowns.map(({ name, promise }) =>
promise.finally(() => {
pendingNames.delete(name)
})
)
).then(() => 'settled' as const)
let timer: ReturnType<typeof setTimeout> | undefined
const deadline = new Promise<'deadline'>((resolve) => {
timer = setTimeout(() => resolve('deadline'), deadlineMs)
timer.unref?.()
})
return Promise.race([Promise.allSettled(teardowns).then(() => undefined), deadline])
const outcome = await Promise.race([settled, deadline])
clearTimeout(timer)
return outcome === 'deadline' ? [...pendingNames] : []
}
+61 -1
View File
@@ -102,6 +102,12 @@ describe('CloudRelayTransport', () => {
existing.push(fn)
listeners.set(event, existing)
}
const removeListener = (event: string, fn: (...args: unknown[]) => void): void => {
listeners.set(
event,
(listeners.get(event) ?? []).filter((listener) => listener !== fn)
)
}
const emit = (event: string, ...args: unknown[]): void => {
for (const fn of listeners.get(event) ?? []) {
fn(...args)
@@ -115,14 +121,17 @@ describe('CloudRelayTransport', () => {
CLOSED: 3,
on: addListener,
once: addListener,
off: removeListener,
send: () => {},
terminate: () => {}
}
const onConnectionClosed = vi.fn()
const transport = new CloudRelayTransport({
cellUrl: 'http://127.0.0.1:9',
relayHostId: 'AbCdEf0123_-xyZ9',
generation: 1,
createSocket: () => fakeSocket as unknown as WebSocketClient
createSocket: () => fakeSocket as unknown as WebSocketClient,
onConnectionClosed
})
const opening = transport.openConnection({
connId: 'conn-1',
@@ -143,6 +152,57 @@ describe('CloudRelayTransport', () => {
await vi.advanceTimersByTimeAsync(1)
await stopPromise
expect(stopped).toBe(true)
expect(onConnectionClosed).toHaveBeenCalledWith('conn-1')
expect(() => transport.metadataFor(fakeSocket as unknown as WebSocketClient)).toThrow(
'unknown_relay_socket'
)
expect(() => transport.setGeneration(2)).not.toThrow()
} finally {
vi.useRealTimers()
}
})
it('observes a synchronous close emitted by terminate without waiting for the deadline', async () => {
vi.useFakeTimers()
try {
const listeners = new Map<string, ((...args: unknown[]) => void)[]>()
const addListener = (event: string, fn: (...args: unknown[]) => void): void => {
listeners.set(event, [...(listeners.get(event) ?? []), fn])
}
const emit = (event: string): void => {
for (const fn of listeners.get(event) ?? []) {
fn()
}
}
const fakeSocket = {
readyState: 1,
OPEN: 1,
CLOSED: 3,
on: addListener,
once: addListener,
off: vi.fn(),
send: () => {},
terminate: () => emit('close')
}
const transport = new CloudRelayTransport({
cellUrl: 'http://127.0.0.1:9',
relayHostId: 'AbCdEf0123_-xyZ9',
generation: 1,
createSocket: () => fakeSocket as unknown as WebSocketClient
})
const opening = transport.openConnection({
connId: 'conn-sync-close',
connTicket: 'ticket-1',
kind: 'resume',
relayDeviceId: 'device-1',
attachDeadlineMs: 1_000
})
emit('open')
await opening
await transport.stop()
expect(vi.getTimerCount()).toBe(0)
} finally {
vi.useRealTimers()
}
+39 -12
View File
@@ -117,10 +117,11 @@ export class CloudRelayTransport implements RpcTransport, MobileSocketTransport
async stop(): Promise<void> {
this.stopped = true
const sockets = [...this.metadataBySocket.keys()]
const closePromises = sockets.map((socket) => this.waitForClose(socket))
for (const socket of sockets) {
socket.terminate()
}
await Promise.all(sockets.map((socket) => this.waitForClose(socket)))
await Promise.all(closePromises)
}
async openConnection(connection: RelayConnectionOpen): Promise<void> {
@@ -158,14 +159,7 @@ export class CloudRelayTransport implements RpcTransport, MobileSocketTransport
}
finalized = true
clearTimeout(deadline)
this.socketsByConnectionId.delete(connection.connId)
this.metadataBySocket.delete(socket)
const clientId = this.clientIds.get(socket) ?? null
this.clientIds.delete(socket)
this.onConnectionClosed?.(connection.connId)
const hasOtherConnections =
clientId !== null && [...this.clientIds.values()].includes(clientId)
this.closeHandler?.(clientId, socket, hasOtherConnections)
this.finalizeConnection(connection.connId, socket)
}
socket.on('message', (raw, isBinary) => {
if (!attached) {
@@ -213,16 +207,49 @@ export class CloudRelayTransport implements RpcTransport, MobileSocketTransport
private waitForClose(socket: WebSocket): Promise<void> {
if (socket.readyState === socket.CLOSED) {
const connectionId = this.connectionIdForSocket(socket)
if (connectionId) {
this.finalizeConnection(connectionId, socket)
}
return Promise.resolve()
}
// Why: a half-open relay socket after system sleep can never emit 'close';
// an unbounded wait here wedges stop() and blocks app quit (#9447).
return new Promise((resolve) => {
const deadline = setTimeout(resolve, RELAY_SOCKET_CLOSE_TIMEOUT_MS)
socket.once('close', () => {
const onClose = (): void => {
clearTimeout(deadline)
resolve()
})
}
const deadline = setTimeout(() => {
socket.off('close', onClose)
const connectionId = this.connectionIdForSocket(socket)
if (connectionId) {
this.finalizeConnection(connectionId, socket)
}
resolve()
}, RELAY_SOCKET_CLOSE_TIMEOUT_MS)
socket.once('close', onClose)
if (socket.readyState === socket.CLOSED) {
onClose()
}
})
}
private connectionIdForSocket(socket: WebSocket): string | undefined {
const metadata = this.metadataBySocket.get(socket)
return metadata?.transport === 'relay' ? metadata.basisConnId : undefined
}
private finalizeConnection(connectionId: string, socket: WebSocket): void {
if (this.socketsByConnectionId.get(connectionId) !== socket) {
return
}
this.socketsByConnectionId.delete(connectionId)
this.metadataBySocket.delete(socket)
const clientId = this.clientIds.get(socket) ?? null
this.clientIds.delete(socket)
this.onConnectionClosed?.(connectionId)
const hasOtherConnections = clientId !== null && [...this.clientIds.values()].includes(clientId)
this.closeHandler?.(clientId, socket, hasOtherConnections)
}
}
+100 -1
View File
@@ -60,7 +60,11 @@ vi.mock('../browser/browser-manager', () => ({
}
}))
import { createMainWindow, loadMainWindow } from './createMainWindow'
import {
createMainWindow,
loadMainWindow,
WINDOW_QUIT_RENDERER_ACK_TIMEOUT_MS
} from './createMainWindow'
import { ipcMain } from 'electron'
import { shouldRecoverRendererAfterProcessGone } from '../crash-reporting/process-gone-classification'
@@ -1804,6 +1808,101 @@ describe('createMainWindow', () => {
})
})
it('destroys an already-unresponsive renderer after an app-wide quit deadline', async () => {
vi.useFakeTimers()
const windowHandlers: Record<string, (...args: any[]) => void> = {}
const webContents = {
id: 42,
on: vi.fn((event, handler) => {
windowHandlers[event] = handler
}),
setZoomLevel: vi.fn(),
setBackgroundThrottling: vi.fn(),
invalidate: vi.fn(),
setWindowOpenHandler: vi.fn(),
send: vi.fn(),
isCrashed: vi.fn(() => false)
}
const destroy = vi.fn()
browserWindowMock.mockImplementation(function () {
return {
webContents,
on: vi.fn((event, handler) => {
windowHandlers[event] = handler
}),
isDestroyed: vi.fn(() => false),
isMaximized: vi.fn(() => true),
isFullScreen: vi.fn(() => false),
getSize: vi.fn(() => [1200, 800]),
setSize: vi.fn(),
maximize: vi.fn(),
show: vi.fn(),
destroy,
loadFile: vi.fn(),
loadURL: vi.fn()
}
})
createMainWindow(null, { getIsQuitting: () => true })
windowHandlers.close({ preventDefault: vi.fn() } as never)
await vi.advanceTimersByTimeAsync(WINDOW_QUIT_RENDERER_ACK_TIMEOUT_MS - 1)
expect(destroy).not.toHaveBeenCalled()
await vi.advanceTimersByTimeAsync(1)
expect(destroy).toHaveBeenCalledOnce()
})
it('keeps the renderer-owned close flow after the quit request is acknowledged', async () => {
vi.useFakeTimers()
const windowHandlers: Record<string, (...args: any[]) => void> = {}
const ipcHandlers: Record<string, (...args: any[]) => void> = {}
vi.mocked(ipcMain.on).mockImplementation((channel, handler) => {
ipcHandlers[channel] = handler as (...args: any[]) => void
return ipcMain
})
const webContents = {
id: 42,
on: vi.fn((event, handler) => {
windowHandlers[event] = handler
}),
setZoomLevel: vi.fn(),
setBackgroundThrottling: vi.fn(),
invalidate: vi.fn(),
setWindowOpenHandler: vi.fn(),
send: vi.fn(),
isCrashed: vi.fn(() => false)
}
const destroy = vi.fn()
browserWindowMock.mockImplementation(function () {
return {
webContents,
on: vi.fn((event, handler) => {
windowHandlers[event] = handler
}),
isDestroyed: vi.fn(() => false),
isMaximized: vi.fn(() => true),
isFullScreen: vi.fn(() => false),
getSize: vi.fn(() => [1200, 800]),
setSize: vi.fn(),
maximize: vi.fn(),
show: vi.fn(),
destroy,
loadFile: vi.fn(),
loadURL: vi.fn()
}
})
createMainWindow(null, { getIsQuitting: () => true })
windowHandlers.close({ preventDefault: vi.fn() } as never)
ipcHandlers['window:close-request-received']?.({ sender: { id: 99 } })
await vi.advanceTimersByTimeAsync(WINDOW_QUIT_RENDERER_ACK_TIMEOUT_MS - 1)
expect(destroy).not.toHaveBeenCalled()
ipcHandlers['window:close-request-received']?.({ sender: { id: 42 } })
await vi.advanceTimersByTimeAsync(1)
expect(destroy).not.toHaveBeenCalled()
})
it('ignores traffic light sync IPC on non-macOS', () => {
const windowHandlers: Record<string, (...args: any[]) => void> = {}
const webContents = {
+41 -1
View File
@@ -51,6 +51,7 @@ import { installPrivilegedWindowNavigationPolicy } from './privileged-window-nav
// Why: show/restore/resume can overlap before the size nudge resets; never capture the temporary width as the next baseline.
const activeRepaintJiggles = new WeakSet<BrowserWindow>()
export const WINDOW_QUIT_RENDERER_ACK_TIMEOUT_MS = 10_000
function forceRepaint(window: BrowserWindow): void {
// Why: webContents can be destroyed a beat before the BrowserWindow during close, and this runs from timers/focus events in that gap.
@@ -849,6 +850,36 @@ export function createMainWindow(
// Intercept close so the renderer can confirm killing running-process terminals (replies window:confirm-close to proceed).
let windowCloseConfirmed = false
const confirmCloseChannel = 'window:confirm-close'
const closeRequestReceivedChannel = 'window:close-request-received'
let quitRendererAckTimer: ReturnType<typeof setTimeout> | null = null
const clearQuitRendererAckTimer = (): void => {
if (quitRendererAckTimer) {
clearTimeout(quitRendererAckTimer)
quitRendererAckTimer = null
}
}
const armQuitRendererAckTimer = (): void => {
if (quitRendererAckTimer) {
return
}
// Why: will-quit cannot run until the renderer-backed window closes; an
// already-frozen renderer otherwise makes Force Quit the only escape.
quitRendererAckTimer = setTimeout(() => {
quitRendererAckTimer = null
if (mainWindow.isDestroyed()) {
return
}
console.warn('[window] Renderer did not acknowledge quit; destroying unresponsive window')
freezeBoundsOnQuit()
mainWindow.destroy()
}, WINDOW_QUIT_RENDERER_ACK_TIMEOUT_MS)
quitRendererAckTimer.unref?.()
}
const onCloseRequestReceived = (event: Electron.IpcMainEvent): void => {
if (event.sender.id === rendererWebContentsId) {
clearQuitRendererAckTimer()
}
}
// Windows minimize-to-tray: hide instead of close when enabled; returns true when it hid so callers skip their close path.
const hideToTrayIfEnabled = (): boolean => {
@@ -909,19 +940,25 @@ export function createMainWindow(
return
}
e.preventDefault()
const isQuitting = opts?.getIsQuitting?.() ?? false
if (isQuitting) {
armQuitRendererAckTimer()
}
// Why: renderer owns the close decision; the always-mounted App root subscription lets even pre-workspace states reply (#5144).
mainWindow.webContents.send('window:close-requested', {
isQuitting: opts?.getIsQuitting?.() ?? false
isQuitting
})
})
mainWindow.webContents.on('will-prevent-unload', () => {
// Why: a prevented beforeunload cancels the quit; release the bounds-persistence freeze so later resizing still saves.
windowClosing = false
clearQuitRendererAckTimer()
opts?.onQuitAborted?.()
mainWindow.webContents.send('window:unload-prevented')
})
const onConfirmClose = (): void => {
clearQuitRendererAckTimer()
windowCloseConfirmed = true
if (!mainWindow.isDestroyed()) {
mainWindow.close()
@@ -980,12 +1017,14 @@ export function createMainWindow(
ipcMain.handle(isMaximizedChannel, onIsMaximized)
ipcMain.on(confirmCloseChannel, onConfirmClose)
ipcMain.on(closeRequestReceivedChannel, onCloseRequestReceived)
mainWindow.on('closed', () => {
// Why: the dashboard pop-out is a companion of the main window — close it
// alongside so it never orphans as a lone window after the app window is
// gone (e.g. on macOS where the app stays alive after the window closes).
closeDashboardPopout()
clearInitialRevealFallbackTimer()
clearQuitRendererAckTimer()
// Why: default-deny the Cmd+B carve-out after the window is gone so a stale-true flag can't leak into later state.
markdownEditorFocused = false
terminalInputFocused = false
@@ -1000,6 +1039,7 @@ export function createMainWindow(
ipcMain.removeListener(popupMenuChannel, onPopupMenu)
ipcMain.removeHandler(isMaximizedChannel)
ipcMain.removeListener(confirmCloseChannel, onConfirmClose)
ipcMain.removeListener(closeRequestReceivedChannel, onCloseRequestReceived)
ipcMain.removeListener(markdownFocusChannel, onMarkdownEditorFocused)
ipcMain.removeListener(terminalInputFocusChannel, onTerminalInputFocused)
ipcMain.removeListener(floatingTerminalInputFocusChannel, onFloatingTerminalInputFocused)
+4 -2
View File
@@ -18,8 +18,10 @@ export type WindowCloseState = {
* therefore cannot answer — bypassing it for a merely-unresponsive renderer is
* what silently destroyed other sessions in #5787. An unresponsive-but-alive
* renderer (rendererProcessGone=false, isRendererCrashed=false) still resolves
* to 'request-confirmation' so the save guard runs. A genuinely gone renderer
* still bypasses so the window stays closable (#5144/#5314).
* to 'request-confirmation' so the save guard runs. App-wide quit separately
* bounds failure to acknowledge that request; ordinary window close does not.
* A genuinely gone renderer still bypasses so the window stays closable
* (#5144/#5314).
*/
export function resolveWindowCloseAction(state: WindowCloseState): WindowCloseAction {
if (state.windowCloseConfirmed) {
+4 -1
View File
@@ -3838,8 +3838,11 @@ const api = {
/** Fired by main when the user tries to close the window; renderer confirms running
* terminals then calls confirmWindowClose(). isQuitting (Cmd+Q / app.quit) skips that dialog. */
onWindowCloseRequested: (callback: (data: { isQuitting: boolean }) => void): (() => void) => {
const listener = (_event: Electron.IpcRendererEvent, data: { isQuitting: boolean }) =>
const listener = (_event: Electron.IpcRendererEvent, data: { isQuitting: boolean }): void => {
// Why: main cannot reach will-quit while a frozen renderer owns the window close handshake.
ipcRenderer.send('window:close-request-received')
callback(data ?? { isQuitting: false })
}
ipcRenderer.on('window:close-requested', listener)
return () => ipcRenderer.removeListener('window:close-requested', listener)
},
@@ -121,6 +121,25 @@ describe('terminal-fit-restore', () => {
}
})
it('bounds bulk restore to one deadline and stops launching work after the transport wedges', async () => {
vi.useFakeTimers()
try {
vi.mocked(getRemoteRuntimeTerminalHandle).mockReturnValue(null)
restoreTerminalFit.mockReturnValue(new Promise(() => {}))
const ptyIds = Array.from({ length: 100 }, (_, index) => `pty-${index}`)
const pending = restoreTerminalFitsToDesktop(ptyIds, undefined)
expect(restoreTerminalFit).toHaveBeenCalledTimes(8)
await vi.advanceTimersByTimeAsync(15_000)
await expect(pending).resolves.toBe(false)
expect(restoreTerminalFit).toHaveBeenCalledTimes(8)
expect(vi.getTimerCount()).toBe(0)
} finally {
vi.useRealTimers()
}
})
it('treats failed remote RPC restore transport as not restored', async () => {
vi.mocked(getRemoteRuntimeTerminalHandle).mockReturnValue('terminal-fail')
vi.mocked(getRemoteRuntimePtyEnvironmentId).mockReturnValue('env-fail')
@@ -128,4 +147,21 @@ describe('terminal-fit-restore', () => {
await expect(restoreTerminalFitToDesktop('remote:pty-fail', undefined)).resolves.toBe(false)
})
it('bounds a remote restore even when the RPC client does not enforce its timeout', async () => {
vi.useFakeTimers()
try {
vi.mocked(getRemoteRuntimeTerminalHandle).mockReturnValue('terminal-stuck')
vi.mocked(getRemoteRuntimePtyEnvironmentId).mockReturnValue('env-stuck')
vi.mocked(callRuntimeRpc).mockReturnValue(new Promise(() => {}))
const pending = restoreTerminalFitToDesktop('remote:pty-stuck', undefined)
await vi.advanceTimersByTimeAsync(15_000)
await expect(pending).resolves.toBe(false)
expect(vi.getTimerCount()).toBe(0)
} finally {
vi.useRealTimers()
}
})
})
@@ -26,11 +26,12 @@ const restoreFailedResult = (): { restored: boolean } => {
// forever, which pins the held-fit modal's buttons disabled (#9447). Fail the
// restore instead so the user can retry.
const withRestoreFitTimeout = async (
pending: Promise<{ restored: boolean }>
pending: Promise<{ restored: boolean }>,
timeoutMs: number
): Promise<{ restored: boolean }> => {
let timer: ReturnType<typeof setTimeout> | undefined
const timedOut = new Promise<{ restored: boolean }>((resolve) => {
timer = setTimeout(() => resolve(restoreFailedResult()), RESTORE_FIT_TIMEOUT_MS)
timer = setTimeout(() => resolve(restoreFailedResult()), timeoutMs)
})
try {
return await Promise.race([pending, timedOut])
@@ -39,35 +40,51 @@ const withRestoreFitTimeout = async (
}
}
export async function restoreTerminalFitToDesktop(
async function restoreTerminalFitToDesktopWithinDeadline(
ptyId: string,
settings: TerminalFitRestoreSettings
settings: TerminalFitRestoreSettings,
deadlineAt: number
): Promise<boolean> {
const timeoutMs = Math.max(0, deadlineAt - Date.now())
if (timeoutMs === 0) {
return false
}
const remoteHandle = getRemoteRuntimeTerminalHandle(ptyId)
const environmentId =
getRemoteRuntimePtyEnvironmentId(ptyId) ?? settings?.activeRuntimeEnvironmentId ?? null
const result =
const pending =
remoteHandle && environmentId
? await callRuntimeRpc<{ restored: boolean }>(
? callRuntimeRpc<{ restored: boolean }>(
{ kind: 'environment', environmentId },
'terminal.restoreFit',
{ terminal: remoteHandle },
{ timeoutMs: RESTORE_FIT_TIMEOUT_MS }
{ timeoutMs }
).catch(restoreFailedResult)
: await withRestoreFitTimeout(
window.api.runtime.restoreTerminalFit(ptyId).catch(restoreFailedResult)
)
: window.api.runtime.restoreTerminalFit(ptyId).catch(restoreFailedResult)
const result = await withRestoreFitTimeout(pending, timeoutMs)
return result.restored
}
export function restoreTerminalFitToDesktop(
ptyId: string,
settings: TerminalFitRestoreSettings
): Promise<boolean> {
return restoreTerminalFitToDesktopWithinDeadline(
ptyId,
settings,
Date.now() + RESTORE_FIT_TIMEOUT_MS
)
}
export async function restoreTerminalFitsToDesktop(
ptyIds: Iterable<string>,
settings: TerminalFitRestoreSettings
): Promise<boolean> {
const uniquePtyIds = [...new Set(ptyIds)]
const deadlineAt = Date.now() + RESTORE_FIT_TIMEOUT_MS
const results = await mapWithConcurrency(uniquePtyIds, RESTORE_FIT_CONCURRENCY, (ptyId) =>
restoreTerminalFitToDesktop(ptyId, settings)
restoreTerminalFitToDesktopWithinDeadline(ptyId, settings, deadlineAt)
)
return results.some(Boolean)
}