This commit is contained in:
2026-10-20 15:48:38 +08:00
parent da6b4685d7
commit 0ff1eefff6
50 changed files with 3933 additions and 78 deletions
+35 -16
View File
@@ -12,27 +12,42 @@ $ErrorActionPreference = 'Stop'
if (-not $IsWindows -or $PSVersionTable.PSVersion.Major -lt 7) {
throw 'local-check requires Windows and PowerShell 7 (pwsh).'
}
# 所有工作目录使用同一用户级锁;句柄随进程退出关闭,不依赖删除过期锁文件。
# 所有工作目录共用两个用户级锁;首槽保留原锁名,运行中的旧入口仍计入容量。
$stateDirectory = Join-Path $env:LOCALAPPDATA 'Chongchong/LocalChecks'
[IO.Directory]::CreateDirectory($stateDirectory) | Out-Null
$lockPath = Join-Path $stateDirectory 'heavy.lock'
$ownerPath = Join-Path $stateDirectory 'owner.json'
$resultPath = Join-Path $stateDirectory 'last-result.json'
function Read-Owner {
try { return [IO.File]::ReadAllText($ownerPath) } catch { return 'Owner information is being updated.' }
$slotDefinitions = @(
[pscustomobject]@{ number = 1; lock = (Join-Path $stateDirectory 'heavy.lock'); owner = (Join-Path $stateDirectory 'owner.json'); result = (Join-Path $stateDirectory 'last-result.json') },
[pscustomobject]@{ number = 2; lock = (Join-Path $stateDirectory 'heavy-2.lock'); owner = (Join-Path $stateDirectory 'owner-2.json'); result = (Join-Path $stateDirectory 'last-result-2.json') }
)
function Read-Owner($definition) {
try { return [IO.File]::ReadAllText($definition.owner) } catch { return 'Owner information is being updated.' }
}
function Open-Slot {
try { return [IO.File]::Open($lockPath, 'OpenOrCreate', 'ReadWrite', 'None') }
function Open-Slot($definition) {
try { return [IO.File]::Open($definition.lock, 'OpenOrCreate', 'ReadWrite', 'None') }
catch [IO.IOException] {
if (($_.Exception.HResult -band 0xffff) -notin @(32, 33)) { throw }
return $null
}
}
function Open-AvailableSlot {
foreach ($definition in $slotDefinitions) {
$handle = Open-Slot $definition
if ($handle) { return [pscustomobject]@{ definition = $definition; handle = $handle } }
}
return $null
}
if ($Status) {
$probe = Open-Slot
if ($probe) { $probe.Dispose(); Write-Host 'IDLE: heavy check slot is free.' }
else { Write-Host 'BUSY:'; Write-Host (Read-Owner) }
if (Test-Path -LiteralPath $resultPath) { Write-Host 'Last result:'; Write-Host ([IO.File]::ReadAllText($resultPath)) }
$busyCount = 0
foreach ($definition in $slotDefinitions) {
$probe = Open-Slot $definition
if ($probe) { $probe.Dispose(); Write-Host "IDLE: slot $($definition.number)/2 is free." }
else { $busyCount++; Write-Host "BUSY: slot $($definition.number)/2"; Write-Host (Read-Owner $definition) }
if (Test-Path -LiteralPath $definition.result) {
Write-Host "Last result for slot $($definition.number):"
try { Write-Host ([IO.File]::ReadAllText($definition.result)) } catch { Write-Host 'Result is being updated.' }
}
}
Write-Host "Capacity: $busyCount/2 slots occupied."
exit 0
}
if (-not $Command -or -not $Command[0]) { throw 'Pass -Command as an array, for example @("pnpm", "typecheck").' }
@@ -51,20 +66,24 @@ $owner = $null
$exitCode = 1
try {
$nextNotice = 0
while (-not ($slot = Open-Slot)) {
while (-not ($lease = Open-AvailableSlot)) {
if ($timer.Elapsed.TotalSeconds -ge $WaitSeconds) {
Write-Host "QUEUE TIMEOUT after $WaitSeconds seconds. No check was started."
exit 75
}
if ($timer.Elapsed.TotalSeconds -ge $nextNotice) {
Write-Host "WAITING for the shared heavy check slot: $(Read-Owner)"
$owners = @($slotDefinitions | ForEach-Object { "slot $($_.number)/2: $(Read-Owner $_)" }) -join ' | '
Write-Host "WAITING for either shared heavy check slot: $owners"
$nextNotice = $timer.Elapsed.TotalSeconds + 15
}
Start-Sleep -Milliseconds 250
}
$owner = [ordered]@{ label = $Label; pid = $PID; directory = $directory; tool = $Command[0]; startedAt = [DateTimeOffset]::Now.ToString('o') }
$slot = $lease.handle
$ownerPath = $lease.definition.owner
$resultPath = $lease.definition.result
$owner = [ordered]@{ label = $Label; pid = $PID; slot = $lease.definition.number; directory = $directory; tool = $Command[0]; startedAt = [DateTimeOffset]::Now.ToString('o') }
[IO.File]::WriteAllText($ownerPath, ($owner | ConvertTo-Json -Compress))
Write-Host "RUNNING [$Label] in $directory (one shared slot; timeout ${TimeoutSeconds}s)"
Write-Host "RUNNING [$Label] in $directory (shared slot $($lease.definition.number)/2; timeout ${TimeoutSeconds}s)"
# Job Object 在包装器被强制结束时也回收本次子进程树,不影响其他 Node/Java。
if (-not ('LocalCheckJob' -as [type])) {