Skip to content
This repository was archived by the owner on May 21, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions docs/adr/0003-how-to-remove-killed-process.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# 3. how to remove killed process

Date: 2022-06-24

## Status

2022-06-24 proposed

2022-06-24 done

## Context

[0002-auto-remove-process](./0002-auto-remove-process-id-menu-item-when-it-does-not-meet-the-threshold.md)

The process cannot be removed when the status changes from draining to killed.

## Decision

Adding a timer to the `monitor` class for checking the time interval and when the cached draining process is long time not updated during slibing window size,
it would send a `remove` signal to the main process.

## Consequences

Consequences here...
1 change: 1 addition & 0 deletions docs/adr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@

* [1. package-tool](0001-package-tool.md)
* [2. auto-remove-process-id-menu-item-when-it-does-not-meet-the-threshold](0002-auto-remove-process-id-menu-item-when-it-does-not-meet-the-threshold.md)
* [3. how-to-remove-killed-process](0003-how-to-remove-killed-process.md)
4 changes: 2 additions & 2 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ changedActions[CHANGED_TYPES.ADD] = ({ pid, stat }) => {
menuTray.setContextMenu(Menu.buildFromTemplate(cachedMenus))
}

changedActions[CHANGED_TYPES.REMOVE] = ({ pid, stat }) => {
logger.warn(`command: ${stat.command} pid: ${pid} becomes normal`)
changedActions[CHANGED_TYPES.REMOVE] = ({ pid }) => {
logger.warn(`pid: ${pid} becomes normal`)
const statusItem = cachedMenus.find(item => item.id === STATUS_ID)
cachedMenus = cachedMenus.filter(item => item.id !== pid)
const hasPid = cachedMenus.some(item => item.isPid)
Expand Down
48 changes: 42 additions & 6 deletions monitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ class Monitor {
this.intervalMs = intervalMs
this.running = false
this.changedCallback = changedCallback
this.cachedPid = new Map()
this.overThresholdPidCache = new Map()
this.windowSize = windowSize
this.stats = new Statistics({
windowSize,
slidingCallback: ({ pid, stat }) => {
Expand All @@ -25,20 +26,30 @@ class Monitor {

const ret = { pid, stat, type: null }
if (median < DEFAULT_THRESHOLD) {
if (!this.cachedPid.has(pid)) return
if (!this.overThresholdPidCache.has(pid)) return

ret.type = CHANGED_TYPES.REMOVE
this.cachedPid.delete(pid)
this.overThresholdPidCache.delete(pid)
} else {
if (this.cachedPid.has(pid)) return
if (this.overThresholdPidCache.has(pid)) {
this.overThresholdPidCache.set(pid, Date.now())
return
}

ret.type = CHANGED_TYPES.ADD
this.cachedPid.set(pid)
this.overThresholdPidCache.set(pid, Date.now())
}

this.changedCallback(ret)
}
})
this.checkTimer = new CheckTimer({
intervalMs: this.windowSize * this.intervalMs,
cache: this.overThresholdPidCache,
cb: ({ pid }) => {
this.changedCallback({ pid, type: CHANGED_TYPES.REMOVE })
}
})
}

start () {
Expand All @@ -60,10 +71,12 @@ class Monitor {
}

interval(this.intervalMs)
this.checkTimer.start()
}

async stop () {
this.running = false
this.checkTimer.stop()
}

isRunning () {
Expand Down Expand Up @@ -108,4 +121,27 @@ class Statistics {
}
}

module.exports = { Monitor, Statistics, CHANGED_TYPES }
class CheckTimer {
constructor ({ intervalMs, cache, cb }) {
this.intervalMs = intervalMs
this.cache = cache
this.cb = cb
}

start () {
this.interval = setInterval(() => {
this.cache.forEach((v, k) => {
if (Date.now() - v < this.intervalMs) return

this.cache.delete(k)
this.cb({ pid: k })
})
}, this.intervalMs)
}

stop () {
clearInterval(this.interval)
}
}

module.exports = { Monitor, Statistics, CheckTimer, CHANGED_TYPES }
33 changes: 32 additions & 1 deletion test/monitor.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const proxyquire = require('proxyquire')

const cpus = [110, 120, 130, 50, 112, 130, 0, 0, 10]
let i = 0
const { Monitor, Statistics, CHANGED_TYPES } = proxyquire('../monitor', {
const { Monitor, Statistics, CheckTimer, CHANGED_TYPES } = proxyquire('../monitor', {
pidtree: async () => [1],
'@reply2future/pidusage': async () => ({ 1: { cpu: cpus[i++ % cpus.length], ppid: 1 } })
})
Expand Down Expand Up @@ -84,4 +84,35 @@ describe('monitor module', () => {
s.addPidStat(1, { ...mockStat })
})
})

describe('checktimer', () => {
let clock
beforeEach(() => {
clock = sinon.useFakeTimers()
})
afterEach(() => {
clock.restore()
})
it('should invoke callback when time interval is bigger than settings', async () => {
const intervalMs = 3000
const cache = new Map([
['expired', Date.now()],
['not expired', Date.now() + intervalMs * 3]
])
let cbCount = 0
const s = new CheckTimer({
intervalMs,
cache,
cb: ({ pid }) => {
pid.should.equal('expired')
cbCount++
}
})

s.start()
await clock.tickAsync(4000)
cbCount.should.equal(1)
s.stop()
})
})
})