@@ -172,14 +172,17 @@ export const kill = (pid: string | number, opts?: TPsNext | TPsKillOptions | TPs
172172
173173 return new Promise < any > ( ( resolve , reject ) => {
174174 let done = false
175+ const entry : TKillEntry = { pid : sPid , registered : 0 , interval, settle : noop }
175176 const settle = ( err ?: unknown ) => {
176177 if ( done ) return
177178 done = true
178179 clearTimeout ( timer )
180+ killPending . delete ( entry )
179181 if ( err ) reject ( err )
180182 else resolve ( pid )
181183 next ?.( err ?? null , pid )
182184 }
185+ entry . settle = settle
183186
184187 const timer = setTimeout ( ( ) => settle ( new Error ( 'Kill process timeout' ) ) , timeout * 1000 )
185188
@@ -190,40 +193,47 @@ export const kill = (pid: string | number, opts?: TPsNext | TPsKillOptions | TPs
190193 return
191194 }
192195
193- let since = Date . now ( )
194- const poll = ( ) : unknown =>
195- sharedSnapshot ( since ) . then ( ( { startedAt, list } ) => {
196- if ( done ) return
197- since = startedAt + 1
198- if ( list . some ( p => p . pid === sPid ) ) {
199- setTimeout ( poll , Math . max ( 0 , startedAt + interval - Date . now ( ) ) )
200- } else {
201- settle ( )
202- }
203- } , settle )
204-
205- poll ( )
196+ entry . registered = Date . now ( )
197+ killPending . add ( entry )
198+ scheduleKillTick ( )
206199 } )
207200}
208201
209202/**
210- * Returns a `lookup()` snapshot started at or after `since`, dedupes concurrent callers.
211- * Lets parallel `kill()` polls share a single `ps` invocation: join the in-flight one
212- * if it's fresh enough, otherwise wait for the next queued one.
203+ * Shared kill-confirmation loop. A single `lookup()` per tick serves *all* pending kills
204+ * registered before the tick's snapshot started — so a flood of kills can never indefinitely
205+ * postpone any single confirmation, and we never spawn more than one `ps` per tick .
213206 */
214- type TSnapshot = { startedAt : number , list : TPsLookupEntry [ ] }
215- let inflight : { startedAt : number , promise : Promise < TSnapshot > } | null = null
216- let queued : Promise < TSnapshot > | null = null
217- const sharedSnapshot = ( since : number ) : Promise < TSnapshot > => {
218- if ( inflight && inflight . startedAt >= since ) return inflight . promise
219- if ( queued ) return queued
220- const after = inflight ?. promise . catch ( noop ) ?? Promise . resolve ( )
221- return queued = after . then ( ( ) => {
222- queued = null
223- const startedAt = Date . now ( )
224- const promise = lookup ( ) . then ( list => ( { startedAt, list } ) )
225- inflight = { startedAt, promise }
226- return promise . finally ( ( ) => { if ( inflight ?. promise === promise ) inflight = null } )
207+ type TKillEntry = { pid : string , registered : number , interval : number , settle : ( err ?: unknown ) => void }
208+ const killPending = new Set < TKillEntry > ( )
209+ let killTickTimer : NodeJS . Timeout | null = null
210+ let killTickRunning = false
211+
212+ const scheduleKillTick = ( lastStart = 0 ) : void => {
213+ if ( killTickTimer || killTickRunning || killPending . size === 0 ) return
214+ let minInterval = Infinity
215+ for ( const k of killPending ) if ( k . interval < minInterval ) minInterval = k . interval
216+ const delay = lastStart === 0 ? 0 : Math . max ( 0 , lastStart + minInterval - Date . now ( ) )
217+ killTickTimer = setTimeout ( runKillTick , delay )
218+ }
219+
220+ const runKillTick = ( ) : void => {
221+ killTickTimer = null
222+ if ( killPending . size === 0 ) return
223+ killTickRunning = true
224+ const startedAt = Date . now ( )
225+ lookup ( ) . then ( list => {
226+ const alive = new Set ( list . map ( p => p . pid ) )
227+ for ( const k of killPending ) {
228+ // Snapshot predates this kill's process.kill — can't trust it, wait for next tick
229+ if ( k . registered >= startedAt ) continue
230+ if ( ! alive . has ( k . pid ) ) k . settle ( )
231+ }
232+ killTickRunning = false
233+ scheduleKillTick ( startedAt )
234+ } , err => {
235+ for ( const k of killPending ) k . settle ( err )
236+ killTickRunning = false
227237 } )
228238}
229239
0 commit comments