diff --git a/BTCPayServer/HostedServices/BaseAsyncService.cs b/BTCPayServer/HostedServices/BaseAsyncService.cs new file mode 100644 index 000000000..2d53c0874 --- /dev/null +++ b/BTCPayServer/HostedServices/BaseAsyncService.cs @@ -0,0 +1,61 @@ +using System; +using Microsoft.Extensions.Logging; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using BTCPayServer.Services; +using BTCPayServer.Services.Rates; +using Microsoft.Extensions.Hosting; +using BTCPayServer.Logging; +using System.Runtime.CompilerServices; +using System.IO; +using System.Text; + +namespace BTCPayServer.HostedServices +{ + public abstract class BaseAsyncService : IHostedService + { + protected CancellationTokenSource _Cts; + protected Task[] _Tasks; + + public Task StartAsync(CancellationToken cancellationToken) + { + _Cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + _Tasks = initializeTasks(); + return Task.CompletedTask; + } + + internal abstract Task[] initializeTasks(); + + protected async Task createLoopTask(Func act, [CallerMemberName]string caller = null) + { + await new SynchronizationContextRemover(); + while (!_Cts.IsCancellationRequested) + { + try + { + await act(); + } + catch (OperationCanceledException) when (_Cts.IsCancellationRequested) + { + } + catch (Exception ex) + { + Logs.PayServer.LogWarning(ex, caller + " failed"); + try + { + await Task.Delay(TimeSpan.FromMinutes(1), _Cts.Token); + } + catch (OperationCanceledException) when (_Cts.IsCancellationRequested) { } + } + } + } + + public Task StopAsync(CancellationToken cancellationToken) + { + _Cts.Cancel(); + return Task.WhenAll(_Tasks); + } + } +} diff --git a/BTCPayServer/HostedServices/RatesHostedService.cs b/BTCPayServer/HostedServices/RatesHostedService.cs index aeee582b3..2ea9c2ea2 100644 --- a/BTCPayServer/HostedServices/RatesHostedService.cs +++ b/BTCPayServer/HostedServices/RatesHostedService.cs @@ -14,7 +14,7 @@ using System.Text; namespace BTCPayServer.HostedServices { - public class RatesHostedService : IHostedService + public class RatesHostedService : BaseAsyncService { private SettingsRepository _SettingsRepository; private IRateProviderFactory _RateProviderFactory; @@ -28,79 +28,40 @@ namespace BTCPayServer.HostedServices _coinAverageSettings = coinAverageSettings; } - - CancellationTokenSource _Cts = new CancellationTokenSource(); - - List _Tasks = new List(); - - public Task StartAsync(CancellationToken cancellationToken) + internal override Task[] initializeTasks() { - _Tasks.Add(RefreshCoinAverageSupportedExchanges(_Cts.Token)); - _Tasks.Add(RefreshCoinAverageSettings(_Cts.Token)); - return Task.CompletedTask; + return new[] + { + createLoopTask(RefreshCoinAverageSupportedExchanges), + createLoopTask(RefreshCoinAverageSettings) + }; } - - async Task Timer(Func act, CancellationToken cancellation, [CallerMemberName]string caller = null) + async Task RefreshCoinAverageSupportedExchanges() { await new SynchronizationContextRemover(); - while (!cancellation.IsCancellationRequested) + var tickers = await new CoinAverageRateProvider("BTC") { Authenticator = _coinAverageSettings }.GetExchangeTickersAsync(); + _coinAverageSettings.AvailableExchanges = tickers + .Exchanges + .Select(c => (c.DisplayName, c.Name)) + .ToArray(); + await Task.Delay(TimeSpan.FromHours(5), _Cts.Token); + } + + async Task RefreshCoinAverageSettings() + { + await new SynchronizationContextRemover(); + var rates = (await _SettingsRepository.GetSettingAsync()) ?? new RatesSetting(); + _RateProviderFactory.CacheSpan = TimeSpan.FromMinutes(rates.CacheInMinutes); + if (!string.IsNullOrWhiteSpace(rates.PrivateKey) && !string.IsNullOrWhiteSpace(rates.PublicKey)) { - try - { - await act(); - } - catch (OperationCanceledException) when (cancellation.IsCancellationRequested) - { - } - catch (Exception ex) - { - Logs.PayServer.LogWarning(ex, caller + " failed"); - try - { - await Task.Delay(TimeSpan.FromMinutes(1), cancellation); - } - catch (OperationCanceledException) when (cancellation.IsCancellationRequested) { } - } + _coinAverageSettings.KeyPair = (rates.PublicKey, rates.PrivateKey); } - } - Task RefreshCoinAverageSupportedExchanges(CancellationToken cancellation) - { - return Timer(async () => + else { - await new SynchronizationContextRemover(); - var tickers = await new CoinAverageRateProvider("BTC") { Authenticator = _coinAverageSettings }.GetExchangeTickersAsync(); - _coinAverageSettings.AvailableExchanges = tickers - .Exchanges - .Select(c => (c.DisplayName, c.Name)) - .ToArray(); - await Task.Delay(TimeSpan.FromHours(5), cancellation); - }, cancellation); - } - - Task RefreshCoinAverageSettings(CancellationToken cancellation) - { - return Timer(async () => - { - await new SynchronizationContextRemover(); - var rates = (await _SettingsRepository.GetSettingAsync()) ?? new RatesSetting(); - _RateProviderFactory.CacheSpan = TimeSpan.FromMinutes(rates.CacheInMinutes); - if (!string.IsNullOrWhiteSpace(rates.PrivateKey) && !string.IsNullOrWhiteSpace(rates.PublicKey)) - { - _coinAverageSettings.KeyPair = (rates.PublicKey, rates.PrivateKey); - } - else - { - _coinAverageSettings.KeyPair = null; - } - await _SettingsRepository.WaitSettingsChanged(cancellation); - }, cancellation); - } - - public Task StopAsync(CancellationToken cancellationToken) - { - _Cts.Cancel(); - return Task.WhenAll(_Tasks.ToArray()); + _coinAverageSettings.KeyPair = null; + } + await _SettingsRepository.WaitSettingsChanged(_Cts.Token); } } }