Dispose worktree change refresh queues (#6040)

Co-authored-by: Neil <neil@stably.ai>
This commit is contained in:
OrcaWin 2026-06-21 21:58:15 -07:00 committed by GitHub
parent 2e03b14cc3
commit c1e071870b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 39 additions and 2 deletions

View File

@ -810,6 +810,7 @@ export function useIpcEvents(): void {
}
}
const worktreeChangeRefreshQueue = createWorktreeChangeRefreshQueue(handleWorktreesChanged)
unsubs.push(worktreeChangeRefreshQueue.dispose)
const activateNotifiedWorktree = async (
{

View File

@ -139,4 +139,30 @@ describe('createWorktreeChangeRefreshQueue', () => {
expect(handler).toHaveBeenNthCalledWith(2, 'repo-1', renamed)
expect(handler).toHaveBeenNthCalledWith(3, 'repo-1', undefined)
})
it('drops queued trailing refreshes after disposal', async () => {
const firstRefresh = deferred()
const handler = vi.fn().mockReturnValueOnce(firstRefresh.promise).mockResolvedValue(undefined)
const queue = createWorktreeChangeRefreshQueue(handler)
queue.enqueue({ repoId: 'repo-1' })
queue.enqueue({ repoId: 'repo-1' })
queue.dispose()
firstRefresh.resolve()
await flushPromises()
expect(handler).toHaveBeenCalledTimes(1)
})
it('ignores new events after disposal', async () => {
const handler = vi.fn(() => Promise.resolve())
const queue = createWorktreeChangeRefreshQueue(handler)
queue.dispose()
queue.enqueue({ repoId: 'repo-1' })
await flushPromises()
expect(handler).not.toHaveBeenCalled()
})
})

View File

@ -20,6 +20,7 @@ type RepoRefreshState = {
}
export type WorktreeChangeRefreshQueue = {
dispose: () => void
enqueue: (event: WorktreeChangeEvent) => void
}
@ -27,11 +28,12 @@ export function createWorktreeChangeRefreshQueue(
handler: WorktreeChangeRefreshHandler
): WorktreeChangeRefreshQueue {
const states = new Map<string, RepoRefreshState>()
let disposed = false
const drain = async (repoId: string, state: RepoRefreshState): Promise<void> => {
state.running = true
try {
while (state.queue.length > 0) {
while (!disposed && state.queue.length > 0) {
const next = state.queue.shift()
try {
await handler(repoId, next?.renamed)
@ -41,7 +43,7 @@ export function createWorktreeChangeRefreshQueue(
}
} finally {
state.running = false
if (state.queue.length === 0) {
if (disposed || state.queue.length === 0) {
states.delete(repoId)
} else {
void drain(repoId, state)
@ -50,7 +52,15 @@ export function createWorktreeChangeRefreshQueue(
}
return {
dispose() {
disposed = true
states.clear()
},
enqueue(event) {
if (disposed) {
return
}
let state = states.get(event.repoId)
if (!state) {
state = { running: false, queue: [] }