Skip to content

Commit 540408a

Browse files
committed
Fix kestrel deadlock in update procedure when using update command
1 parent 9058898 commit 540408a

File tree

1 file changed

+32
-2
lines changed

1 file changed

+32
-2
lines changed

ArchiSteamFarm/IPC/Controllers/Api/CommandController.cs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
using System;
2525
using System.Net;
26+
using System.Threading;
2627
using System.Threading.Tasks;
2728
using ArchiSteamFarm.Core;
2829
using ArchiSteamFarm.IPC.Requests;
@@ -31,11 +32,20 @@
3132
using ArchiSteamFarm.Steam;
3233
using ArchiSteamFarm.Storage;
3334
using Microsoft.AspNetCore.Mvc;
35+
using Microsoft.Extensions.Hosting;
3436

3537
namespace ArchiSteamFarm.IPC.Controllers.Api;
3638

3739
[Route("Api/Command")]
3840
public sealed class CommandController : ArchiController {
41+
private readonly IHostApplicationLifetime ApplicationLifetime;
42+
43+
public CommandController(IHostApplicationLifetime applicationLifetime) {
44+
ArgumentNullException.ThrowIfNull(applicationLifetime);
45+
46+
ApplicationLifetime = applicationLifetime;
47+
}
48+
3949
/// <summary>
4050
/// Executes a command.
4151
/// </summary>
@@ -72,8 +82,28 @@ public async Task<ActionResult<GenericResponse>> CommandPost([FromBody] CommandR
7282
command = command[commandPrefix.Length..];
7383
}
7484

75-
string? response = await targetBot.Commands.Response(EAccess.Owner, command).ConfigureAwait(false);
85+
// Update process can result in kestrel shutdown request, just before patching the files
86+
// In this case, we have very little opportunity to do anything, especially we will not have access to the return value of the command
87+
// That's because update command will synchronously stop the kestrel, and wait for it before proceeding with an update, and that'll wait for us finishing the request, never happening
88+
// Therefore, we'll allow this command to proceed while listening for application shutdown request, if it happens, we'll do our best by getting alternative signal that update is proceeding
89+
TaskCompletionSource<bool> applicationStopping = new();
90+
91+
CancellationTokenRegistration applicationStoppingRegistration = ApplicationLifetime.ApplicationStopping.Register(() => applicationStopping.SetResult(true));
7692

77-
return Ok(new GenericResponse<string>(response));
93+
await using (applicationStoppingRegistration.ConfigureAwait(false)) {
94+
Task<string?> commandTask = targetBot.Commands.Response(EAccess.Owner, command);
95+
96+
string? response;
97+
98+
if (await Task.WhenAny(commandTask, applicationStopping.Task).ConfigureAwait(false) == commandTask) {
99+
response = await commandTask.ConfigureAwait(false);
100+
} else {
101+
// It's almost guaranteed that this is the result of update process requesting kestrel shutdown
102+
// However, we're still going to check PendingVersionUpdate, which should be set by the update process as alternative way to inform us about pending update
103+
response = ASFController.PendingVersionUpdate != null ? Strings.PatchingFiles : Strings.Exiting;
104+
}
105+
106+
return Ok(new GenericResponse<string>(response));
107+
}
78108
}
79109
}

0 commit comments

Comments
 (0)