Files
orca/docs/audits/runtime-rpc-consumed-queue/extended-controls.cjs
T

231 lines
7.0 KiB
JavaScript

const assert = require('node:assert/strict')
const fs = require('node:fs')
const path = require('node:path')
const { load, collect, sha, versions } = require('./queue-source.cjs')
const PAYLOAD_BYTES = 1024 * 1024
function liveCall(queue, method = 'git.status') {
const hold = Promise.withResolvers()
return {
release: () => hold.resolve(),
settled: queue.enqueue('fixture', method, () => hold.promise)
}
}
function payloadCall(queue, method, hold, signal, throwSynchronously = false) {
const payload = new Uint8Array(PAYLOAD_BYTES)
payload[0] = 19
const ref = new WeakRef(payload)
const settled = queue.enqueue(
'fixture',
method,
() => {
if (throwSynchronously) {
throw new Error(`fixture failure ${payload[0]}`)
}
return hold.then(() => payload[0])
},
payload.byteLength,
signal
)
return { ref, settled }
}
function liveCount(refs) {
return refs.filter((ref) => ref.deref() !== undefined).length
}
async function checkActiveAndCancelled(Queue, candidate, lane) {
const queue = new Queue(1, 1)
const method = lane === 'foreground' ? 'terminal.send' : 'git.status'
const hold = Promise.withResolvers()
const active = payloadCall(queue, method, hold.promise)
const controller = new AbortController()
const queued = payloadCall(queue, method, Promise.resolve(), controller.signal)
const rejected = assert.rejects(queued.settled, { name: 'AbortError' })
await collect()
assert.equal(liveCount([active.ref, queued.ref]), 2)
assert.equal(queue.retainedCallBytes, 2 * PAYLOAD_BYTES)
controller.abort()
await rejected
await collect()
assert.equal(liveCount([active.ref]), 1)
assert.equal(liveCount([queued.ref]), 0)
assert.equal(queue.retainedCallBytes, PAYLOAD_BYTES)
assert.equal(queue.queuedCallCount, 0)
hold.resolve()
assert.equal(await active.settled, 19)
await collect()
assert.equal(liveCount([active.ref, queued.ref]), 0)
assert.equal(queue.retainedCallBytes, 0)
assert.equal(queue.queues.size, 0)
return {
candidate,
case: 'active-and-cancelled',
lane,
activeRetainedUntilSettlement: true,
cancelledReleasedBeforeActiveFinishes: true
}
}
async function checkFailure(Queue, candidate, lane) {
const queue = new Queue(3, 2)
const hold = liveCall(queue, 'terminal.send')
const method = lane === 'foreground' ? 'terminal.send' : 'git.status'
let failed = payloadCall(queue, method, Promise.resolve(), undefined, true)
const ref = failed.ref
await assert.rejects(failed.settled, { message: 'fixture failure 19' })
failed = null
await collect()
const retained = liveCount([ref])
assert.equal(retained, candidate ? 0 : 1)
assert.equal(queue.retainedCallBytes, 0)
hold.release()
await hold.settled
await collect()
assert.equal(liveCount([ref]), 0)
assert.equal(queue.queues.size, 0)
return {
candidate,
case: 'synchronous-failure',
lane,
retainedWhileOtherCallActive: retained,
retainedAfterIdle: 0
}
}
async function checkCrossLaneTraffic(Queue, candidate) {
const queue = new Queue(3, 2)
let current = liveCall(queue)
const refs = []
for (let index = 0; index < 8; index++) {
const item = payloadCall(queue, 'terminal.send', Promise.resolve())
refs.push(item.ref)
assert.equal(await item.settled, 19)
}
for (let index = 0; index < 70; index++) {
const next = liveCall(queue)
current.release()
await current.settled
current = next
}
await collect()
const retained = liveCount(refs)
assert.equal(retained, candidate ? 0 : 8)
assert.equal(queue.retainedCallBytes, 0)
assert.equal(queue.queues.get('fixture').active, 1)
assert.equal(queue.queues.get('fixture').foregroundHead, 8)
assert.equal(queue.queues.get('fixture').backgroundHead, 5)
current.release()
await current.settled
await collect()
assert.equal(liveCount(refs), 0)
assert.equal(queue.queues.size, 0)
return {
candidate,
case: 'rolling-background-traffic',
completedForegroundPayloads: 8,
completedBackgroundCalls: 70,
foregroundPayloadsStillRetained: retained,
retainedAfterIdle: 0,
everyBackgroundCallCompletes: true
}
}
async function checkOrderAndCompaction(Queue, candidate) {
const queue = new Queue(1, 1)
const blocker = liveCall(queue, 'terminal.send')
const started = []
const pending = []
const controllers = []
for (const lane of ['background', 'foreground']) {
for (let index = 0; index < 70; index++) {
const id = `${lane}:${index}`
const controller = new AbortController()
const promise = queue.enqueue(
'fixture',
lane === 'background' ? 'git.status' : 'terminal.send',
async () => {
started.push(id)
return id
},
0,
controller.signal
)
pending.push(
promise.then(
(value) => ({ value }),
(error) => ({ error: error.name })
)
)
controllers.push({ id, controller })
}
}
const cancelled = new Set([
'foreground:0',
'foreground:35',
'foreground:69',
'background:0',
'background:35',
'background:69'
])
for (const { id, controller } of controllers) {
if (cancelled.has(id)) {
controller.abort()
}
}
assert.equal(queue.queuedCallCount, 134)
blocker.release()
await blocker.settled
const results = await Promise.all(pending)
const expected = ['foreground', 'background']
.flatMap((lane) => Array.from({ length: 70 }, (_, index) => `${lane}:${index}`))
.filter((id) => !cancelled.has(id))
assert.deepEqual(started, expected)
assert.equal(results.filter((result) => result.error === 'AbortError').length, 6)
assert.equal(results.filter((result) => result.value !== undefined).length, 134)
await collect()
assert.equal(queue.queuedCallCount, 0)
assert.equal(queue.queues.size, 0)
return {
candidate,
case: 'ordering-compaction-and-cancellation',
completed: 134,
cancelled: 6,
foregroundBeforeBackground: true,
fifoWithinEachLane: true,
finalQueueCount: 0
}
}
async function main() {
const reports = []
for (const candidate of [false, true]) {
const Queue = load(candidate)
for (const lane of ['foreground', 'background']) {
reports.push(await checkActiveAndCancelled(Queue, candidate, lane))
reports.push(await checkFailure(Queue, candidate, lane))
}
reports.push(await checkCrossLaneTraffic(Queue, candidate))
reports.push(await checkOrderAndCompaction(Queue, candidate))
}
const report = {
node: process.version,
electron: process.versions.electron ?? null,
versions,
proofSha256: sha(fs.readFileSync(__filename)),
reports
}
const resultName = process.versions.electron
? 'electron-extended-results.json'
: 'extended-results.json'
fs.writeFileSync(path.join(__dirname, resultName), `${JSON.stringify(report, null, 2)}\n`)
console.log(JSON.stringify(report, null, 2))
}
main().catch((error) => {
console.error(error)
process.exitCode = 1
})
setTimeout(() => {
console.error('fixture timeout')
process.exit(2)
}, 10000).unref()