[CmdletBinding()] param( [ValidateRange(2,5)][int]$PhaseCount = 5, [ValidateSet('all','seams','native','analysis')][string]$Mode = 'all', [ValidateRange(0,1875)][int]$SeamRowLimit = 0 ) $ErrorActionPreference = 'Stop' $WorkDir = [IO.Path]::GetFullPath($PSScriptRoot) $Exe = Join-Path $WorkDir 'bin\SharpMesherSpike.exe' $OutDir = Join-Path $WorkDir 'results' if (-not (Test-Path -LiteralPath $Exe -PathType Leaf)) { throw "Spike executable missing: $Exe" } [IO.Directory]::CreateDirectory($OutDir) | Out-Null $Runner = Get-Process -Id $PID $Runner.PriorityClass = 'BelowNormal' $Runner.ProcessorAffinity = [IntPtr]0xFF00 $Affinity = [IntPtr]0xFF00 $Stdout = Join-Path $WorkDir 'run.stdout.log' $Stderr = Join-Path $WorkDir 'run.stderr.log' $EightGB = 8L * 1024L * 1024L * 1024L $Attempt = 0 do { $Attempt++ Remove-Item -LiteralPath $Stdout, $Stderr -Force -ErrorAction SilentlyContinue $Info = [Diagnostics.ProcessStartInfo]::new() $Info.FileName = $Exe $ModeArgument = if ($Mode -eq 'seams') { ' --seams-only' } elseif ($Mode -eq 'native') { ' --native-only' } elseif ($Mode -eq 'analysis') { ' --analysis-only' } else { '' } $RowLimitArgument = if ($SeamRowLimit -gt 0) { ' --seam-row-limit={0}' -f $SeamRowLimit } else { '' } $Info.Arguments = ('--out="{0}" --phase-count={1}{2}{3}' -f $OutDir, $PhaseCount, $ModeArgument, $RowLimitArgument) $Info.WorkingDirectory = [IO.Path]::GetFullPath((Join-Path $WorkDir '..\..')) $Info.UseShellExecute = $false $Info.CreateNoWindow = $true $Info.RedirectStandardOutput = $true $Info.RedirectStandardError = $true $Process = [Diagnostics.Process]::new() $Process.StartInfo = $Info if (-not $Process.Start()) { throw 'Could not start SharpMesherSpike.exe.' } $Process.PriorityClass = [Diagnostics.ProcessPriorityClass]::BelowNormal $Process.ProcessorAffinity = $Affinity $StartAt = [DateTime]::UtcNow $OutputTask = $Process.StandardOutput.ReadToEndAsync() $ErrorTask = $Process.StandardError.ReadToEndAsync() $TimedOut = $false $OverMemory = $false while (-not $Process.HasExited) { $Process.Refresh() if ($Process.PrivateMemorySize64 -ge $EightGB) { $OverMemory = $true; break } if (([DateTime]::UtcNow - $StartAt).TotalSeconds -ge 900) { $TimedOut = $true; break } Start-Sleep -Milliseconds 250 } if ($TimedOut -or $OverMemory) { try { $Process.Kill() } catch {} } $Process.WaitForExit() $RunStdout = $OutputTask.GetAwaiter().GetResult() $RunStderr = $ErrorTask.GetAwaiter().GetResult() [IO.File]::WriteAllText($Stdout, $RunStdout) [IO.File]::WriteAllText($Stderr, $RunStderr) if ($TimedOut) { throw "Spike exceeded 15 minutes and was stopped. Logs: $Stdout / $Stderr" } if ($OverMemory) { throw "Spike reached the 8 GB private-memory cap and was stopped. Logs: $Stdout / $Stderr" } if ($Process.ExitCode -eq -1073741502 -and $Attempt -eq 1) { Write-Output 'Spike exited 0xC0000142; waiting 60 seconds before the one allowed retry.' Start-Sleep -Seconds 60 continue } if ($Process.ExitCode -ne 0) { Write-Output "Spike failed exit_code=$($Process.ExitCode) stdout=$Stdout stderr=$Stderr" if ($RunStdout) { Write-Output $RunStdout } if ($RunStderr) { Write-Output $RunStderr } throw 'Sharp mesher spike failed.' } } while ($Process.ExitCode -eq -1073741502 -and $Attempt -lt 2) Write-Output "Spike passed elapsed_seconds=$([Math]::Round(([DateTime]::UtcNow - $StartAt).TotalSeconds,2)) phase_count=$PhaseCount priority=BelowNormal affinity=0xFF00 stdout=$Stdout stderr=$Stderr"