Skip to content

Commit

Permalink
Fix kestrel deadlock in update procedure when using update command
Browse files Browse the repository at this point in the history
  • Loading branch information
JustArchi committed Nov 10, 2024
1 parent 9058898 commit 540408a
Showing 1 changed file with 32 additions and 2 deletions.
34 changes: 32 additions & 2 deletions ArchiSteamFarm/IPC/Controllers/Api/CommandController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.IPC.Requests;
Expand All @@ -31,11 +32,20 @@
using ArchiSteamFarm.Steam;
using ArchiSteamFarm.Storage;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Hosting;

namespace ArchiSteamFarm.IPC.Controllers.Api;

[Route("Api/Command")]
public sealed class CommandController : ArchiController {
private readonly IHostApplicationLifetime ApplicationLifetime;

public CommandController(IHostApplicationLifetime applicationLifetime) {
ArgumentNullException.ThrowIfNull(applicationLifetime);

ApplicationLifetime = applicationLifetime;
}

/// <summary>
/// Executes a command.
/// </summary>
Expand Down Expand Up @@ -72,8 +82,28 @@ public async Task<ActionResult<GenericResponse>> CommandPost([FromBody] CommandR
command = command[commandPrefix.Length..];
}

string? response = await targetBot.Commands.Response(EAccess.Owner, command).ConfigureAwait(false);
// Update process can result in kestrel shutdown request, just before patching the files
// In this case, we have very little opportunity to do anything, especially we will not have access to the return value of the command
// 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
// 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
TaskCompletionSource<bool> applicationStopping = new();

CancellationTokenRegistration applicationStoppingRegistration = ApplicationLifetime.ApplicationStopping.Register(() => applicationStopping.SetResult(true));

return Ok(new GenericResponse<string>(response));
await using (applicationStoppingRegistration.ConfigureAwait(false)) {
Task<string?> commandTask = targetBot.Commands.Response(EAccess.Owner, command);

string? response;

if (await Task.WhenAny(commandTask, applicationStopping.Task).ConfigureAwait(false) == commandTask) {
response = await commandTask.ConfigureAwait(false);
} else {
// It's almost guaranteed that this is the result of update process requesting kestrel shutdown
// 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
response = ASFController.PendingVersionUpdate != null ? Strings.PatchingFiles : Strings.Exiting;
}

return Ok(new GenericResponse<string>(response));
}
}
}

0 comments on commit 540408a

Please sign in to comment.