Use BackgroundService instead of old pattern
MODIFY
WalletWasabi.Gui/Global.cs
WalletWasabi.Gui/Global.cs
@@ -370,7 +370,7 @@ namespace WalletWasabi.Gui
if (jsonRpcServerConfig.IsEnabled)
{
RpcServer = new JsonRpcServer(this, jsonRpcServerConfig);
- RpcServer.Start();
+ await RpcServer.StartAsync(cancel).ConfigureAwait(false);
}
#endregion JsonRpcServerInitialization
@@ -820,8 +820,9 @@ namespace WalletWasabi.Gui
var rpcServer = RpcServer;
if (rpcServer is {})
{
- rpcServer.Stop();
- Logger.LogInfo($"{nameof(rpcServer)} is stopped.", nameof(Global));
+ using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(21));
+ await rpcServer.StopAsync(cts.Token);
+ Logger.LogInfo($"{nameof(RpcServer)} is stopped.", nameof(Global));
}
var feeProviders = FeeProviders;

MODIFY
WalletWasabi.Gui/Rpc/JsonRpcServer.cs
WalletWasabi.Gui/Rpc/JsonRpcServer.cs
@@ -4,15 +4,14 @@ using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.Extensions.Hosting;
+using WalletWasabi.Logging;
using WalletWasabi.Gui.Models;
namespace WalletWasabi.Gui.Rpc
{
- public class JsonRpcServer
+ public class JsonRpcServer : BackgroundService
{
- private long _running;
- public bool IsRunning => Interlocked.Read(ref _running) == 1;
- public bool IsStopping => Interlocked.Read(ref _running) == 2;
private CancellationTokenSource Cancellation { get; }
private HttpListener Listener { get; }
@@ -32,32 +31,39 @@ namespace WalletWasabi.Gui.Rpc
Service = new WasabiJsonRpcService(global);
}
- public void Start()
+ public override async Task StartAsync(CancellationToken cancellationToken)
{
- Interlocked.Exchange(ref _running, 1);
Listener.Start();
+ await base.StartAsync(cancellationToken);
+ }
- Task.Factory.StartNew(async _ =>
+ public override async Task StopAsync(CancellationToken cancellationToken)
{
- try
+ await base.StopAsync(cancellationToken);
+ Listener.Stop();
+ }
+
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var handler = new JsonRpcRequestHandler<WasabiJsonRpcService>(Service);
- while (IsRunning)
+ while (!stoppingToken.IsCancellationRequested)
+ {
+ try
{
- var context = Listener.GetContext();
+ var context = await GetHttpContext(stoppingToken);
var request = context.Request;
var response = context.Response;
if (request.HttpMethod == "POST")
{
using var reader = new StreamReader(request.InputStream);
- string body = await reader.ReadToEndAsync();
+ string body = await reader.ReadToEndAsync().ConfigureAwait(false);
var identity = (HttpListenerBasicIdentity)context.User?.Identity;
if (!Config.RequiresCredentials || CheckValidCredentials(identity))
{
- var result = await handler.HandleAsync(body, Cancellation.Token);
+ var result = await handler.HandleAsync(body, stoppingToken).ConfigureAwait(false);
// result is null only when the request is a notification.
if (!string.IsNullOrEmpty(result))
@@ -65,8 +71,8 @@ namespace WalletWasabi.Gui.Rpc
response.ContentType = "application/json-rpc";
var output = response.OutputStream;
var buffer = Encoding.UTF8.GetBytes(result);
- await output.WriteAsync(buffer, 0, buffer.Length);
- await output.FlushAsync();
+ await output.WriteAsync(buffer, 0, buffer.Length, stoppingToken);
+ await output.FlushAsync(stoppingToken);
}
}
else
@@ -80,24 +86,30 @@ namespace WalletWasabi.Gui.Rpc
}
response.Close();
}
+ catch(OperationCanceledException)
+ {
+ break;
}
- finally
+ catch(Exception ex)
{
- Interlocked.CompareExchange(ref _running, 3, 2); // If IsStopping, make it stopped.
+ Logger.LogError(ex);
+ }
}
- }, Cancellation.Token, TaskCreationOptions.LongRunning);
}
- internal void Stop()
+ private async Task<HttpListenerContext> GetHttpContext(CancellationToken cancellationToken)
{
- Interlocked.CompareExchange(ref _running, 2, 1); // If running, make it stopping.;
- Listener.Stop();
- Cancellation.Cancel();
- while (IsStopping)
+ var getHttpContextTask = Listener.GetContextAsync();
+ var tcs = new TaskCompletionSource<bool>();
+ using (cancellationToken.Register(state => ((TaskCompletionSource<bool>)state).TrySetResult(true), tcs))
+ {
+ var firstTaskToComplete = await Task.WhenAny(getHttpContextTask, tcs.Task).ConfigureAwait(false);
+ if (getHttpContextTask != firstTaskToComplete)
{
- Task.Delay(50).GetAwaiter().GetResult();
+ cancellationToken.ThrowIfCancellationRequested();
+ }
}
- Cancellation.Dispose();
+ return await getHttpContextTask;
}
private bool CheckValidCredentials(HttpListenerBasicIdentity identity)

Use BackgroundService instead of old pattern