Skip to content

Commit b798e00

Browse files
committed
Improved build cancellation.
1 parent e9181b1 commit b798e00

File tree

3 files changed

+46
-10
lines changed

3 files changed

+46
-10
lines changed

src/PostSharp.Engineering.BuildTools/BaseCommand.cs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ public sealed override int Execute( CommandContext context, T settings )
2929
CancellationTokenSource? timeoutCancellation = null;
3030

3131
var mainCancellation = new CancellationTokenSource();
32-
Console.CancelKeyPress += ( _, _ ) => mainCancellation.Cancel();
3332

3433
try
3534
{
@@ -50,6 +49,8 @@ public sealed override int Execute( CommandContext context, T settings )
5049
buildContext = buildContext.WithUseProjectDirectoryAsWorkingDirectory( true );
5150
}
5251

52+
Console.CancelKeyPress += ( _, _ ) => OnCancel( buildContext, mainCancellation );
53+
5354
// Sets up a timeout. The Timer class does not support long periods, so we use CancellationTokenSource.
5455
if ( settings.Timeout != null )
5556
{
@@ -156,6 +157,8 @@ public sealed override int Execute( CommandContext context, T settings )
156157

157158
if ( buildContext.CancellationToken.IsCancellationRequested )
158159
{
160+
buildContext.Console.WriteError( "The build was cancelled." );
161+
159162
return (int) ExitCode.Cancelled;
160163
}
161164

@@ -180,6 +183,34 @@ public sealed override int Execute( CommandContext context, T settings )
180183

181184
protected abstract bool ExecuteCore( BuildContext context, T settings );
182185

186+
private static void OnCancel( BuildContext buildContext, CancellationTokenSource mainCancellation )
187+
{
188+
var console = buildContext.Console;
189+
190+
if ( RuntimeInformation.IsOSPlatform( OSPlatform.Windows ) )
191+
{
192+
console.WriteError( $"Cancelling and killing all child processes." );
193+
194+
// List all child processes.
195+
var processes = ProcessHelper.GetProcessTree( console, Process.GetCurrentProcess().Id );
196+
197+
console.WriteMessage( "Process tree:" );
198+
199+
foreach ( var node in processes )
200+
{
201+
var indent = new string( '-', (node.NestingLevel + 1) * 3 );
202+
console.WriteMessage( $"+{indent} {node.Process.Id} {ProcessHelper.GetCommandLine( node.Process )}" );
203+
}
204+
205+
// Kill all processes (except the current one) in reverse order.
206+
ProcessHelper.KillProcesses( console, processes.Reverse().Select( x => x.Process ) );
207+
}
208+
209+
// Signal the main cancellation source.
210+
// We don't exit the process so exception handlers and finally blocks can run.
211+
mainCancellation.Cancel();
212+
}
213+
183214
private static void OnTimeout( BuildContext buildContext, Stopwatch stopwatch, CancellationTokenSource mainCancellation )
184215
{
185216
var console = buildContext.Console;
@@ -212,8 +243,13 @@ private static void OnTimeout( BuildContext buildContext, Stopwatch stopwatch, C
212243
else
213244
{
214245
console.WriteError( $"The process timed out after {stopwatch.Elapsed}. Exiting." );
246+
mainCancellation.Cancel();
215247
}
216248

249+
// Give the normal cancellation workflow a chance to complete.
250+
Thread.Sleep( 10 );
251+
252+
// If we're still here, we're abruptly terminating the process.
217253
console.WriteWarning( "Terminating the current process." );
218254
Environment.FailFast( $"The process timed out after {stopwatch.Elapsed}." );
219255
}

src/PostSharp.Engineering.BuildTools/Resources/DockerBuild.ps1

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@ function New-EnvJson
9090
return $jsonPath
9191
}
9292

93+
if ($env:RUNNING_IN_DOCKER)
94+
{
95+
Write-Error "Already running in Docker."
96+
exit 1
97+
}
98+
9399
# Generate ImageName from script directory if not provided
94100
if ( [string]::IsNullOrEmpty($ImageName))
95101
{
@@ -103,7 +109,8 @@ if ( [string]::IsNullOrEmpty($ImageName))
103109
$ImageTag = "docker-build-image"
104110
}
105111
Write-Host "Generated image name from directory: $ImageTag" -ForegroundColor Cyan
106-
} else
112+
}
113+
else
107114
{
108115
# Generate a hash of the repo directory tagging (4 bytes, 8 hex chars)
109116
$hashBytes = (New-Object -TypeName System.Security.Cryptography.SHA256Managed).ComputeHash([System.Text.Encoding]::UTF8.GetBytes($PSScriptRoot))
@@ -264,7 +271,7 @@ Write-Host "Git directories: " $gitDirectoriesAsString -ForegroundColor Gray
264271
# Kill all containers
265272
docker ps -q --filter "ancestor=$ImageTag" | ForEach-Object {
266273
Write-Host "Killing container $_"
267-
docker kill $_
274+
docker kill $_
268275
}
269276

270277
# Building the image.

src/PostSharp.Engineering.BuildTools/Utilities/ConsoleHelper.cs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,6 @@ public class ConsoleHelper
1414
{
1515
private static readonly CancellationTokenSource _cancellationTokenSource = new();
1616

17-
static ConsoleHelper()
18-
{
19-
Console.CancelKeyPress += OnCancel;
20-
}
21-
22-
private static void OnCancel( object? sender, ConsoleCancelEventArgs e ) => _cancellationTokenSource.Cancel();
23-
2417
public static void Cancel() => _cancellationTokenSource.Cancel();
2518

2619
// This now de facto a synonym of BuildContext.CancellationToken, but unifying this requires many changes.

0 commit comments

Comments
 (0)