From ef0b8376d3f845f96785d5a0ad2bf4e22bb41cc1 Mon Sep 17 00:00:00 2001 From: lepipele Date: Mon, 23 Apr 2018 17:40:36 -0500 Subject: [PATCH 1/4] Abstracting hosted service that has listen loop tasks --- .../HostedServices/BaseAsyncService.cs | 61 ++++++++++++ .../HostedServices/RatesHostedService.cs | 93 ++++++------------- 2 files changed, 88 insertions(+), 66 deletions(-) create mode 100644 BTCPayServer/HostedServices/BaseAsyncService.cs 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); } } } From 2e5d29064b0acdae7878940dc3ebf3c97e5d2a7e Mon Sep 17 00:00:00 2001 From: lepipele Date: Mon, 23 Apr 2018 17:42:03 -0500 Subject: [PATCH 2/4] Removing CssThemeManager dependency on ServerController Using newly created BaseAsyncService to listen for database changes of theme setting --- BTCPayServer/Controllers/ServerController.cs | 8 +-- .../HostedServices/CssThemeManager.cs | 68 +++++++++++-------- BTCPayServer/Hosting/BTCPayServerServices.cs | 4 +- 3 files changed, 44 insertions(+), 36 deletions(-) diff --git a/BTCPayServer/Controllers/ServerController.cs b/BTCPayServer/Controllers/ServerController.cs index b13839ab7..08c41e878 100644 --- a/BTCPayServer/Controllers/ServerController.cs +++ b/BTCPayServer/Controllers/ServerController.cs @@ -25,17 +25,14 @@ namespace BTCPayServer.Controllers private UserManager _UserManager; SettingsRepository _SettingsRepository; private IRateProviderFactory _RateProviderFactory; - private CssThemeManager _CssThemeManager; public ServerController(UserManager userManager, IRateProviderFactory rateProviderFactory, - SettingsRepository settingsRepository, - CssThemeManager cssThemeManager) + SettingsRepository settingsRepository) { _UserManager = userManager; _SettingsRepository = settingsRepository; _RateProviderFactory = rateProviderFactory; - _CssThemeManager = cssThemeManager; } [Route("server/rates")] @@ -234,9 +231,6 @@ namespace BTCPayServer.Controllers public async Task Theme(ThemeSettings settings) { await _SettingsRepository.UpdateSetting(settings); - // TODO: remove controller/class-level property and have only reference to - // CssThemeManager here in this method - _CssThemeManager.Update(settings); TempData["StatusMessage"] = "Theme settings updated successfully"; return View(settings); } diff --git a/BTCPayServer/HostedServices/CssThemeManager.cs b/BTCPayServer/HostedServices/CssThemeManager.cs index 09f733d44..ca845d723 100644 --- a/BTCPayServer/HostedServices/CssThemeManager.cs +++ b/BTCPayServer/HostedServices/CssThemeManager.cs @@ -16,47 +16,59 @@ namespace BTCPayServer.HostedServices { public class CssThemeManager { - public CssThemeManager(SettingsRepository settingsRepository) - { - Update(settingsRepository); - } - - private async void Update(SettingsRepository settingsRepository) - { - var data = (await settingsRepository.GetSettingAsync()) ?? new ThemeSettings(); - Update(data); - } - public void Update(ThemeSettings data) { - UpdateBootstrap(data.BootstrapCssUri); - UpdateCreativeStart(data.CreativeStartCssUri); - } + if (String.IsNullOrWhiteSpace(data.BootstrapCssUri)) + _bootstrapUri = "/vendor/bootstrap4/css/bootstrap.css?v=" + DateTime.Now.Ticks; + else + _bootstrapUri = data.BootstrapCssUri; - private string _bootstrapUri = "/vendor/bootstrap4/css/bootstrap.css?v=" + DateTime.Now.Ticks; + + if (String.IsNullOrWhiteSpace(data.CreativeStartCssUri)) + _creativeStartUri = "/vendor/bootstrap4-creativestart/creative.css?v=" + DateTime.Now.Ticks; + else + _creativeStartUri = data.CreativeStartCssUri; + } + + private string _bootstrapUri; public string BootstrapUri { get { return _bootstrapUri; } } - public void UpdateBootstrap(string newUri) - { - if (String.IsNullOrWhiteSpace(newUri)) - _bootstrapUri = "/vendor/bootstrap4/css/bootstrap.css?v="+ DateTime.Now.Ticks; - else - _bootstrapUri = newUri; - } - private string _creativeStartUri = "/vendor/bootstrap4-creativestart/creative.css?v=" + DateTime.Now.Ticks; + private string _creativeStartUri; public string CreativeStartUri { get { return _creativeStartUri; } } - public void UpdateCreativeStart(string newUri) + } + + public class CssThemeManagerHostedService : BaseAsyncService + { + private SettingsRepository _SettingsRepository; + private CssThemeManager _CssThemeManager; + + public CssThemeManagerHostedService(SettingsRepository settingsRepository, CssThemeManager cssThemeManager) { - if (String.IsNullOrWhiteSpace(newUri)) - _creativeStartUri = "/vendor/bootstrap4-creativestart/creative.css?v=" + DateTime.Now.Ticks; - else - _creativeStartUri = newUri; + _SettingsRepository = settingsRepository; + _CssThemeManager = cssThemeManager; + } + + internal override Task[] initializeTasks() + { + return new[] + { + createLoopTask(ListenForThemeChanges) + }; + } + + async Task ListenForThemeChanges() + { + await new SynchronizationContextRemover(); + var data = (await _SettingsRepository.GetSettingAsync()) ?? new ThemeSettings(); + _CssThemeManager.Update(data); + + await _SettingsRepository.WaitSettingsChanged(_Cts.Token); } } } diff --git a/BTCPayServer/Hosting/BTCPayServerServices.cs b/BTCPayServer/Hosting/BTCPayServerServices.cs index b9c4b36b1..f2ce5b78a 100644 --- a/BTCPayServer/Hosting/BTCPayServerServices.cs +++ b/BTCPayServer/Hosting/BTCPayServerServices.cs @@ -138,7 +138,6 @@ namespace BTCPayServer.Hosting services.TryAddSingleton(); services.TryAddSingleton(); - services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); @@ -148,6 +147,9 @@ namespace BTCPayServer.Hosting BlockTarget = 20 }); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton, Payments.Bitcoin.BitcoinLikePaymentHandler>(); services.AddSingleton(); From fbc4ca89aa60e02260031c11e357bd9dd0f1f18f Mon Sep 17 00:00:00 2001 From: lepipele Date: Thu, 26 Apr 2018 21:44:21 -0500 Subject: [PATCH 3/4] Enapsulating Token per code review discussions --- BTCPayServer/HostedServices/BaseAsyncService.cs | 9 +++++++-- BTCPayServer/HostedServices/CssThemeManager.cs | 2 +- BTCPayServer/HostedServices/RatesHostedService.cs | 4 ++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/BTCPayServer/HostedServices/BaseAsyncService.cs b/BTCPayServer/HostedServices/BaseAsyncService.cs index 2d53c0874..dd2ad5016 100644 --- a/BTCPayServer/HostedServices/BaseAsyncService.cs +++ b/BTCPayServer/HostedServices/BaseAsyncService.cs @@ -16,18 +16,23 @@ namespace BTCPayServer.HostedServices { public abstract class BaseAsyncService : IHostedService { - protected CancellationTokenSource _Cts; + private CancellationTokenSource _Cts; protected Task[] _Tasks; public Task StartAsync(CancellationToken cancellationToken) { - _Cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + _Cts = new CancellationTokenSource(); _Tasks = initializeTasks(); return Task.CompletedTask; } internal abstract Task[] initializeTasks(); + protected CancellationToken _SyncToken + { + get { return _Cts.Token; } + } + protected async Task createLoopTask(Func act, [CallerMemberName]string caller = null) { await new SynchronizationContextRemover(); diff --git a/BTCPayServer/HostedServices/CssThemeManager.cs b/BTCPayServer/HostedServices/CssThemeManager.cs index ca845d723..adc9fce67 100644 --- a/BTCPayServer/HostedServices/CssThemeManager.cs +++ b/BTCPayServer/HostedServices/CssThemeManager.cs @@ -68,7 +68,7 @@ namespace BTCPayServer.HostedServices var data = (await _SettingsRepository.GetSettingAsync()) ?? new ThemeSettings(); _CssThemeManager.Update(data); - await _SettingsRepository.WaitSettingsChanged(_Cts.Token); + await _SettingsRepository.WaitSettingsChanged(_SyncToken); } } } diff --git a/BTCPayServer/HostedServices/RatesHostedService.cs b/BTCPayServer/HostedServices/RatesHostedService.cs index 2ea9c2ea2..4fea30c06 100644 --- a/BTCPayServer/HostedServices/RatesHostedService.cs +++ b/BTCPayServer/HostedServices/RatesHostedService.cs @@ -45,7 +45,7 @@ namespace BTCPayServer.HostedServices .Exchanges .Select(c => (c.DisplayName, c.Name)) .ToArray(); - await Task.Delay(TimeSpan.FromHours(5), _Cts.Token); + await Task.Delay(TimeSpan.FromHours(5), _SyncToken); } async Task RefreshCoinAverageSettings() @@ -61,7 +61,7 @@ namespace BTCPayServer.HostedServices { _coinAverageSettings.KeyPair = null; } - await _SettingsRepository.WaitSettingsChanged(_Cts.Token); + await _SettingsRepository.WaitSettingsChanged(_SyncToken); } } } From 042142396d14411e8dda871fc9c90ce2d6b262a0 Mon Sep 17 00:00:00 2001 From: lepipele Date: Thu, 26 Apr 2018 21:52:04 -0500 Subject: [PATCH 4/4] Refactoring code to adhere to naming guidelines --- BTCPayServer/HostedServices/BaseAsyncService.cs | 8 ++++---- BTCPayServer/HostedServices/CssThemeManager.cs | 6 +++--- BTCPayServer/HostedServices/RatesHostedService.cs | 10 +++++----- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/BTCPayServer/HostedServices/BaseAsyncService.cs b/BTCPayServer/HostedServices/BaseAsyncService.cs index dd2ad5016..4b8c85512 100644 --- a/BTCPayServer/HostedServices/BaseAsyncService.cs +++ b/BTCPayServer/HostedServices/BaseAsyncService.cs @@ -22,18 +22,18 @@ namespace BTCPayServer.HostedServices public Task StartAsync(CancellationToken cancellationToken) { _Cts = new CancellationTokenSource(); - _Tasks = initializeTasks(); + _Tasks = InitializeTasks(); return Task.CompletedTask; } - internal abstract Task[] initializeTasks(); + internal abstract Task[] InitializeTasks(); - protected CancellationToken _SyncToken + protected CancellationToken Cancellation { get { return _Cts.Token; } } - protected async Task createLoopTask(Func act, [CallerMemberName]string caller = null) + protected async Task CreateLoopTask(Func act, [CallerMemberName]string caller = null) { await new SynchronizationContextRemover(); while (!_Cts.IsCancellationRequested) diff --git a/BTCPayServer/HostedServices/CssThemeManager.cs b/BTCPayServer/HostedServices/CssThemeManager.cs index adc9fce67..e3afcf35b 100644 --- a/BTCPayServer/HostedServices/CssThemeManager.cs +++ b/BTCPayServer/HostedServices/CssThemeManager.cs @@ -54,11 +54,11 @@ namespace BTCPayServer.HostedServices _CssThemeManager = cssThemeManager; } - internal override Task[] initializeTasks() + internal override Task[] InitializeTasks() { return new[] { - createLoopTask(ListenForThemeChanges) + CreateLoopTask(ListenForThemeChanges) }; } @@ -68,7 +68,7 @@ namespace BTCPayServer.HostedServices var data = (await _SettingsRepository.GetSettingAsync()) ?? new ThemeSettings(); _CssThemeManager.Update(data); - await _SettingsRepository.WaitSettingsChanged(_SyncToken); + await _SettingsRepository.WaitSettingsChanged(Cancellation); } } } diff --git a/BTCPayServer/HostedServices/RatesHostedService.cs b/BTCPayServer/HostedServices/RatesHostedService.cs index 4fea30c06..dcb0b404d 100644 --- a/BTCPayServer/HostedServices/RatesHostedService.cs +++ b/BTCPayServer/HostedServices/RatesHostedService.cs @@ -28,12 +28,12 @@ namespace BTCPayServer.HostedServices _coinAverageSettings = coinAverageSettings; } - internal override Task[] initializeTasks() + internal override Task[] InitializeTasks() { return new[] { - createLoopTask(RefreshCoinAverageSupportedExchanges), - createLoopTask(RefreshCoinAverageSettings) + CreateLoopTask(RefreshCoinAverageSupportedExchanges), + CreateLoopTask(RefreshCoinAverageSettings) }; } @@ -45,7 +45,7 @@ namespace BTCPayServer.HostedServices .Exchanges .Select(c => (c.DisplayName, c.Name)) .ToArray(); - await Task.Delay(TimeSpan.FromHours(5), _SyncToken); + await Task.Delay(TimeSpan.FromHours(5), Cancellation); } async Task RefreshCoinAverageSettings() @@ -61,7 +61,7 @@ namespace BTCPayServer.HostedServices { _coinAverageSettings.KeyPair = null; } - await _SettingsRepository.WaitSettingsChanged(_SyncToken); + await _SettingsRepository.WaitSettingsChanged(Cancellation); } } }