code clean, make Global a better palce
MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/HistoryTabViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/HistoryTabViewModel.cs
@@ -128,6 +128,10 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
foreach (SmartCoin coin in Global.WalletService.Coins)
{
var found = txRecordList.FirstOrDefault(x => x.transactionId == coin.TransactionId);
+
+ if (Global.WalletService is null) //disposed meanwhile
+ break;
+
SmartTransaction foundTransaction = Global.WalletService.TransactionCache.First(x => x.GetHash() == coin.TransactionId);
DateTimeOffset dateTime;
if (foundTransaction.Height.Type == HeightType.Chain)

MODIFY
WalletWasabi.Gui/Dialogs/CannotCloseDialogViewModel.cs
WalletWasabi.Gui/Dialogs/CannotCloseDialogViewModel.cs
@@ -60,7 +60,7 @@ namespace WalletWasabi.Gui.Dialogs
// OK pressed.
Close(false);
},
- canOk).DisposeWith(Disposables);
+ canOk);
CancelCommand = ReactiveCommand.CreateFromTask(async () =>
{
@@ -73,7 +73,7 @@ namespace WalletWasabi.Gui.Dialogs
// OK pressed.
Close(false);
},
- canCancel).DisposeWith(Disposables);
+ canCancel);
_cancelTokenSource = new CancellationTokenSource();
Disposables.Add(_cancelTokenSource);
@@ -105,6 +105,7 @@ namespace WalletWasabi.Gui.Dialogs
while (!last)
{
last = DateTime.Now - start > TimeSpan.FromMinutes(2);
+ if (_cancelTokenSource.IsCancellationRequested) break;
try
{
await Global.DesperateDequeueAllCoinsAsync();

MODIFY
WalletWasabi.Gui/MainWindow.xaml
WalletWasabi.Gui/MainWindow.xaml
WindowState ="{Binding WindowState}"
UseLayoutRounding="True" RenderOptions.BitmapInterpolationMode="HighQuality"
id:DragBehavior.IsEnabled="False" id:DropBehavior.IsEnabled="False">
- <i:Interaction.Behaviors>
- <behaviors:PredicateCloseBehavior CanClose="{Binding CanClose}" />
- </i:Interaction.Behaviors>
<cont:MetroWindow.TitleBarContent>
<menu:MainMenuView DataContext="{Binding Shell.MainMenu}" Margin="4 0 0 0" Foreground="{DynamicResource ThemeForegroundBrush}" VerticalAlignment="Stretch" FontSize="13" />
</cont:MetroWindow.TitleBarContent>

MODIFY
WalletWasabi.Gui/MainWindow.xaml.cs
WalletWasabi.Gui/MainWindow.xaml.cs
@@ -3,6 +3,7 @@ using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
+using System.Threading;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
@@ -46,37 +47,51 @@ namespace WalletWasabi.Gui
AvaloniaXamlLoader.Load(this);
}
- private async void MainWindow_ClosingAsync(object sender, CancelEventArgs e)
+ private Task _closingTask;
+ private long _closingState;
+
+ private void MainWindow_ClosingAsync(object sender, CancelEventArgs e)
{
- try
+ e.Cancel = true;
+ switch (Interlocked.Read(ref _closingState))
+ {
+ case 0:
+ Interlocked.Increment(ref _closingState);
+ _closingTask = ClosingAsync();
+ break;
+
+ case 1:
+ // still closing cancel the progress
+ return;
+
+ case 2:
+ e.Cancel = false;
+ return; //can close the window
+ }
+ }
+
+ private async Task ClosingAsync()
{
- if (IsQuitPending) return;
- IsQuitPending = true;
+ bool closeApplication = false;
try
{
if (Global.ChaumianClient != null)
- Global.ChaumianClient.IsQuitPending = true;
- bool closeApplication = false;
+ Global.ChaumianClient.IsQuitPending = true; //indicate -> do not add any more alices to the coinjoin
if (!MainWindowViewModel.Instance.CanClose)
{
using (var dialog = new CannotCloseDialogViewModel())
{
- closeApplication = await MainWindowViewModel.Instance.ShowDialogAsync(dialog);
+ closeApplication = await MainWindowViewModel.Instance.ShowDialogAsync(dialog); //start the deque process with a dialog
}
}
else
closeApplication = true;
- e.Cancel = !closeApplication;
- }
- finally
+ if (closeApplication)
+ {
+ try
{
- if (Global.ChaumianClient != null)
- Global.ChaumianClient.IsQuitPending = false;
- IsQuitPending = false;
- }
-
Global.UiConfig.WindowState = WindowState;
Global.UiConfig.Width = Width;
Global.UiConfig.Height = Height;
@@ -87,6 +102,25 @@ namespace WalletWasabi.Gui
{
Logging.Logger.LogWarning<MainWindow>(ex);
}
+ Interlocked.Exchange(ref _closingState, 2); //now we can close the app
+ Close(); //start the closing process. Will call MainWindow_ClosingAsync again!
+ }
+ //let's go to finally
+ }
+ catch (Exception ex)
+ {
+ Interlocked.Exchange(ref _closingState, 0); //something happened back to starting point
+ Logging.Logger.LogWarning<MainWindow>(ex);
+ }
+ finally
+ {
+ if (!closeApplication) //we are not closing the application for some reason
+ {
+ Interlocked.Exchange(ref _closingState, 0);
+ if (Global.ChaumianClient != null)
+ Global.ChaumianClient.IsQuitPending = false; //re-enable enqueuing coins
+ }
+ }
}
#pragma warning disable IDE1006 // Naming Styles

code clean, make Global a better palce