Skip to content

Commit 90a0fb0

Browse files
committed
refactor: optimize internal polling flow
1 parent 34a5808 commit 90a0fb0

1 file changed

Lines changed: 43 additions & 33 deletions

File tree

src/main/ts/ps.ts

Lines changed: 43 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,27 @@ const _tree = ({ cb = noop, opts, sync = false }: {
153153
}
154154
}
155155

156+
/**
157+
* Returns a `lookup()` snapshot started at or after `since`, dedupes concurrent callers.
158+
* Lets parallel `kill()` polls share a single `ps` invocation: join the in-flight one
159+
* if it's fresh enough, otherwise wait for the next queued one.
160+
*/
161+
type TSnapshot = { startedAt: number, list: TPsLookupEntry[] }
162+
let inflight: { startedAt: number, promise: Promise<TSnapshot> } | null = null
163+
let queued: Promise<TSnapshot> | null = null
164+
const sharedSnapshot = (since: number): Promise<TSnapshot> => {
165+
if (inflight && inflight.startedAt >= since) return inflight.promise
166+
if (queued) return queued
167+
const after = inflight?.promise.catch(noop) ?? Promise.resolve()
168+
return queued = after.then(() => {
169+
queued = null
170+
const startedAt = Date.now()
171+
const promise = lookup().then(list => ({ startedAt, list }))
172+
inflight = { startedAt, promise }
173+
return promise.finally(() => { inflight = inflight?.promise === promise ? null : inflight })
174+
})
175+
}
176+
156177
export const pickTree = (list: TPsLookupEntry[], pid: string | number, recursive = false): TPsLookupEntry[] => {
157178
const children = list.filter(p => p.ppid === String(pid))
158179
return [
@@ -173,48 +194,37 @@ export const kill = (pid: string | number, opts?: TPsNext | TPsKillOptions | TPs
173194

174195
const { promise, resolve, reject } = makeDeferred()
175196
const { timeout = 30, signal = 'SIGTERM', interval = 200 } = opts || {}
197+
const sPid = String(pid)
198+
let done = false
199+
const settle = (err?: unknown) => {
200+
if (done) return
201+
done = true
202+
clearTimeout(timer)
203+
err ? reject(err) : resolve(pid)
204+
next?.(err ?? null, pid)
205+
}
176206

207+
let timer: NodeJS.Timeout
177208
try {
178209
process.kill(+pid, signal)
179210
} catch (e) {
180-
reject(e)
181-
next?.(e)
211+
settle(e)
182212
return promise
183213
}
184214

185-
let confirmCount = 0
186-
let timedOut = false
187-
188-
const timer = setTimeout(() => {
189-
timedOut = true
190-
const err = new Error('Kill process timeout')
191-
reject(err)
192-
next?.(err)
193-
}, timeout * 1000)
194-
195-
const poll = () =>
196-
lookup({ pid }, (err, list = []) => {
197-
if (timedOut) return
198-
if (err) {
199-
clearTimeout(timer)
200-
reject(err)
201-
next?.(err, pid)
202-
return
203-
}
204-
if (list.length > 0) {
205-
confirmCount = Math.max(confirmCount - 1, 0)
206-
setTimeout(poll, interval)
207-
return
208-
}
209-
confirmCount++
210-
if (confirmCount >= 5) {
211-
clearTimeout(timer)
212-
resolve(pid)
213-
next?.(null, pid)
215+
let since = Date.now()
216+
timer = setTimeout(() => settle(new Error('Kill process timeout')), timeout * 1000)
217+
218+
const poll = (): unknown =>
219+
sharedSnapshot(since).then(({ startedAt, list }) => {
220+
if (done) return
221+
since = startedAt + 1
222+
if (list.some(p => p.pid === sPid)) {
223+
setTimeout(poll, Math.max(0, startedAt + interval - Date.now()))
214224
} else {
215-
setTimeout(poll, interval)
225+
settle()
216226
}
217-
})
227+
}, settle)
218228

219229
poll()
220230
return promise

0 commit comments

Comments
 (0)