Add disposal to IndexStore
MODIFY
WalletWasabi.Gui/Global.cs
WalletWasabi.Gui/Global.cs
@@ -761,6 +761,17 @@ namespace WalletWasabi.Gui
Logger.LogError($"Error during the disposal of {nameof(SingleInstanceChecker)}: {ex}");
}
+ Logger.LogDebug($"Step: {nameof(SingleInstanceChecker)}.", nameof(Global));
+
+ try
+ {
+ await BitcoinStore.DisposeAsync().ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ Logger.LogError($"Error during the disposal of {nameof(BitcoinStore)}: {ex}");
+ }
+
Logger.LogDebug($"Step: {nameof(AsyncMutex)}.", nameof(Global));
if (AsyncMutex.IsAny)

MODIFY
WalletWasabi.Tests/IntegrationTests/P2pTests.cs
WalletWasabi.Tests/IntegrationTests/P2pTests.cs
@@ -56,7 +56,7 @@ namespace WalletWasabi.Tests.IntegrationTests
var transactionStore = new AllTransactionStore(Path.Combine(dataDir, "transactionStore"), network);
var mempoolService = new MempoolService();
var blocks = new FileSystemBlockRepository(Path.Combine(dataDir, "blocks"), network);
- BitcoinStore bitcoinStore = new BitcoinStore(indexStore, transactionStore, mempoolService, blocks);
+ await using BitcoinStore bitcoinStore = new BitcoinStore(indexStore, transactionStore, mempoolService, blocks);
await bitcoinStore.InitializeAsync();
var addressManagerFolderPath = Path.Combine(dataDir, "AddressManager");
@@ -167,6 +167,8 @@ namespace WalletWasabi.Tests.IntegrationTests
Logger.LogInfo($"Saved {nameof(AddressManager)} to `{addressManagerFilePath}`.");
await syncer?.StopAsync();
+
+ await bitcoinStore.DisposeAsync();
}
}
}

MODIFY
WalletWasabi.Tests/UnitTests/BitcoinCore/P2pBasedTests.cs
WalletWasabi.Tests/UnitTests/BitcoinCore/P2pBasedTests.cs
@@ -28,6 +28,7 @@ namespace WalletWasabi.Tests.UnitTests.BitcoinCore
using var services = new HostedServices();
CoreNode coreNode = await TestNodeBuilder.CreateAsync(services);
await services.StartAllAsync();
+ BitcoinStore? bitcoinStore = null;
using var node = await coreNode.CreateNewP2pNodeAsync();
try
@@ -41,7 +42,7 @@ namespace WalletWasabi.Tests.UnitTests.BitcoinCore
var blocks = new FileSystemBlockRepository(Path.Combine(dir, "blocks"), network);
// Construct BitcoinStore.
- var bitcoinStore = new BitcoinStore(indexStore, transactionStore, mempoolService, blocks);
+ bitcoinStore = new BitcoinStore(indexStore, transactionStore, mempoolService, blocks);
await bitcoinStore.InitializeAsync();
await rpc.GenerateAsync(blockCount: 101);
@@ -87,6 +88,10 @@ namespace WalletWasabi.Tests.UnitTests.BitcoinCore
}
finally
{
+ if (bitcoinStore is { } store)
+ {
+ await store.DisposeAsync();
+ }
await services.StopAllAsync();
node.Disconnect();
await coreNode.TryStopAsync();

MODIFY
WalletWasabi.Tests/UnitTests/Filters/IndexStoreTests.cs
WalletWasabi.Tests/UnitTests/Filters/IndexStoreTests.cs
@@ -25,8 +25,9 @@ namespace WalletWasabi.Tests.UnitTests.Filters
{
Directory.Delete(dir, true);
}
- var indexStore = new IndexStore(dir, network, new SmartHeaderChain());
+ await using var indexStore = new IndexStore(dir, network, new SmartHeaderChain());
await indexStore.InitializeAsync();
+ await indexStore.DisposeAsync();
}
[Fact]
@@ -37,7 +38,7 @@ namespace WalletWasabi.Tests.UnitTests.Filters
var network = Network.Main;
var headersChain = new SmartHeaderChain();
- var indexStore = new IndexStore(dir, network, headersChain);
+ await using var indexStore = new IndexStore(dir, network, headersChain);
var dummyFilter = GolombRiceFilter.Parse("00");
static DateTimeOffset MinutesAgo(int mins) => DateTimeOffset.UtcNow.Subtract(TimeSpan.FromMinutes(mins));
@@ -64,7 +65,7 @@ namespace WalletWasabi.Tests.UnitTests.Filters
var network = Network.Main;
var headersChain = new SmartHeaderChain();
- var indexStore = new IndexStore(dir, network, headersChain);
+ await using var indexStore = new IndexStore(dir, network, headersChain);
var dummyFilter = GolombRiceFilter.Parse("00");
@@ -94,7 +95,7 @@ namespace WalletWasabi.Tests.UnitTests.Filters
var network = Network.Main;
var headersChain = new SmartHeaderChain();
- var indexStore = new IndexStore(dir, network, headersChain);
+ await using var indexStore = new IndexStore(dir, network, headersChain);
var dummyFilter = GolombRiceFilter.Parse("00");
@@ -127,7 +128,7 @@ namespace WalletWasabi.Tests.UnitTests.Filters
var network = Network.Main;
var headersChain = new SmartHeaderChain();
- var indexStore = new IndexStore(dir, network, headersChain);
+ await using var indexStore = new IndexStore(dir, network, headersChain);
var dummyFilter = GolombRiceFilter.Parse("00");

MODIFY
WalletWasabi/Stores/BitcoinStore.cs
WalletWasabi/Stores/BitcoinStore.cs
using NBitcoin;
+using System;
+using System.Threading;
using System.Threading.Tasks;
using WalletWasabi.Blockchain.Blocks;
using WalletWasabi.Blockchain.Mempool;
@@ -13,7 +15,7 @@ namespace WalletWasabi.Stores
/// The purpose of this class is to safely and efficiently manage all the Bitcoin related data
/// that's being serialized to disk, like transactions, wallet files, keys, blocks, index files, etc.
/// </summary>
- public class BitcoinStore
+ public class BitcoinStore : IAsyncDisposable
{
public BitcoinStore(
IndexStore indexStore,
@@ -56,5 +58,10 @@ namespace WalletWasabi.Stores
IsInitialized = true;
}
}
+
+ public async ValueTask DisposeAsync()
+ {
+ await IndexStore.DisposeAsync().ConfigureAwait(false);
+ }
}
}

MODIFY
WalletWasabi/Stores/IndexStore.cs
WalletWasabi/Stores/IndexStore.cs
@@ -19,7 +19,7 @@ namespace WalletWasabi.Stores
/// <summary>
/// Manages to store the filters safely.
/// </summary>
- public class IndexStore
+ public class IndexStore : IAsyncDisposable
{
private int _throttleId;
@@ -277,6 +277,8 @@ namespace WalletWasabi.Stores
foreach (var filter in filters)
{
var success = false;
+
+ ThrowIfDisposed();
using (await IndexLock.LockAsync().ConfigureAwait(false))
{
success = TryProcessFilter(filter, enqueue: true);
@@ -299,6 +301,7 @@ namespace WalletWasabi.Stores
{
FilterModel filter = null;
+ ThrowIfDisposed();
using (await IndexLock.LockAsync().ConfigureAwait(false))
{
filter = ImmatureFilters.Last();
@@ -362,6 +365,7 @@ namespace WalletWasabi.Stores
{
try
{
+ ThrowIfDisposed();
// If throttle is requested, then throttle.
if (throttle != TimeSpan.Zero)
{
@@ -386,6 +390,8 @@ namespace WalletWasabi.Stores
Interlocked.Exchange(ref _throttleId, 0); // So to notify the currently throttled threads that they do not have to run.
}
+ ThrowIfDisposed();
+
using (await MatureIndexAsyncLock.LockAsync(cancel).ConfigureAwait(false))
using (await ImmatureIndexAsyncLock.LockAsync(cancel).ConfigureAwait(false))
using (await IndexLock.LockAsync(cancel).ConfigureAwait(false))
@@ -414,6 +420,8 @@ namespace WalletWasabi.Stores
public async Task ForeachFiltersAsync(Func<FilterModel, Task> todo, Height fromHeight, CancellationToken cancel = default)
{
+ ThrowIfDisposed();
+
using (await MatureIndexAsyncLock.LockAsync(cancel).ConfigureAwait(false))
using (await IndexLock.LockAsync(cancel).ConfigureAwait(false))
{
@@ -488,5 +496,33 @@ namespace WalletWasabi.Stores
}
}
}
+
+ private void ThrowIfDisposed()
+ {
+ if (_disposed)
+ {
+ throw new ObjectDisposedException(nameof(IndexStore));
+ }
+ }
+
+ private bool _disposed;
+
+ public async ValueTask DisposeAsync()
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ // Indicate that the object is disposed.
+ _disposed = true;
+
+ // Wait for the ongoing operations to finish, like TryCommitToFileAsync.
+ using (await MatureIndexAsyncLock.LockAsync().ConfigureAwait(false))
+ using (await ImmatureIndexAsyncLock.LockAsync().ConfigureAwait(false))
+ using (await IndexLock.LockAsync().ConfigureAwait(false))
+ {
+ }
+ }
}
}

Add disposal to IndexStore