Skip to content

Commit 8b17b7d

Browse files
committed
Add double-Dispose guards
1 parent f44a766 commit 8b17b7d

File tree

5 files changed

+64
-3
lines changed

5 files changed

+64
-3
lines changed

src/xunit.runner.visualstudio/Sinks/VsDiscoverySink.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#pragma warning disable CA1513 // ObjectDisposedException.ThrowIf is not available in net472
2+
13
using System;
24
using System.Collections.Generic;
35
using System.Globalization;
@@ -28,6 +30,8 @@ internal sealed class VsDiscoverySink : IVsDiscoverySink, IDisposable
2830
readonly VsTestCaseDiscoverySink discoverySink;
2931
readonly DiscoveryEventSink discoveryEventSink = new();
3032
readonly Dictionary<string, string> displayNamesByTestCaseUniqueID = [];
33+
readonly object disposalLock = new();
34+
bool disposed;
3135
readonly LoggerHelper logger;
3236
readonly string source;
3337
readonly List<ITestCaseDiscovered> testCaseBatch = [];
@@ -59,8 +63,18 @@ public VsDiscoverySink(
5963

6064
public int TotalTests { get; private set; }
6165

62-
public void Dispose() =>
66+
public void Dispose()
67+
{
68+
lock (disposalLock)
69+
{
70+
if (disposed)
71+
throw new ObjectDisposedException(nameof(VsDiscoverySink));
72+
73+
disposed = true;
74+
}
75+
6376
Finished.Dispose();
77+
}
6478

6579
public static VsTestCase? CreateVsTestCase(
6680
string source,

src/xunit.runner.visualstudio/Sinks/VsExecutionSink.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#pragma warning disable CA1513 // ObjectDisposedException.ThrowIf is not available in net472
2+
13
using System;
24
using System.Collections.Concurrent;
35
using System.Collections.Generic;
@@ -23,6 +25,8 @@ internal sealed class VsExecutionSink : TestMessageSink, IDisposable
2325
static readonly HashSet<char> InvalidFileNameChars = Path.GetInvalidFileNameChars().ToHashSet();
2426

2527
readonly Func<bool> cancelledThunk;
28+
readonly object disposalLock = new();
29+
bool disposed;
2630
readonly LoggerHelper logger;
2731
readonly IMessageSink innerSink;
2832
readonly ConcurrentDictionary<string, MessageMetadataCache> metadataCacheByAssemblyID = [];
@@ -95,6 +99,14 @@ public VsExecutionSink(
9599

96100
public void Dispose()
97101
{
102+
lock (disposalLock)
103+
{
104+
if (disposed)
105+
throw new ObjectDisposedException(nameof(VsExecutionSink));
106+
107+
disposed = true;
108+
}
109+
98110
Finished.Dispose();
99111
}
100112

src/xunit.runner.visualstudio/Utility/AppDomainManager.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ namespace Xunit.Runner.VisualStudio;
1313
class AppDomainManager
1414
{
1515
readonly AppDomain appDomain;
16+
readonly object disposalLock = new();
17+
bool disposed;
1618

1719
public AppDomainManager(string assemblyFileName)
1820
{
@@ -54,6 +56,14 @@ public AppDomainManager(string assemblyFileName)
5456

5557
public virtual void Dispose()
5658
{
59+
lock (disposalLock)
60+
{
61+
if (disposed)
62+
throw new ObjectDisposedException(nameof(AppDomainManager));
63+
64+
disposed = true;
65+
}
66+
5767
if (appDomain is not null)
5868
{
5969
try

src/xunit.runner.visualstudio/Utility/DiaSessionWrapper.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#pragma warning disable CA1513 // ObjectDisposedException.ThrowIf is not available in net472
2+
13
using System;
24
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
35
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Navigation;
@@ -14,9 +16,11 @@ class DiaSessionWrapper : IDisposable
1416
#if NETFRAMEWORK
1517
readonly AppDomainManager? appDomainManager;
1618
#endif
19+
readonly DiagnosticMessageSink diagnosticMessageSink;
20+
readonly object disposalLock = new();
21+
bool disposed;
1722
readonly DiaSessionWrapperHelper? helper;
1823
readonly DiaSession? session;
19-
readonly DiagnosticMessageSink diagnosticMessageSink;
2024

2125
public DiaSessionWrapper(
2226
string assemblyFileName,
@@ -73,6 +77,14 @@ public DiaSessionWrapper(
7377

7478
public void Dispose()
7579
{
80+
lock (disposalLock)
81+
{
82+
if (disposed)
83+
throw new ObjectDisposedException(nameof(DiaSessionWrapper));
84+
85+
disposed = true;
86+
}
87+
7688
session?.Dispose();
7789
#if NETFRAMEWORK
7890
appDomainManager?.Dispose();

src/xunit.runner.visualstudio/Utility/VisualStudioSourceInformationProvider.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
#pragma warning disable CA1513 // ObjectDisposedException.ThrowIf is not available in net472
2+
3+
using System;
14
using System.Threading.Tasks;
25
using Xunit.Runner.Common;
36
using Xunit.Sdk;
@@ -17,7 +20,9 @@ internal class VisualStudioSourceInformationProvider(
1720
{
1821
static readonly SourceInformation EmptySourceInformation = new();
1922

20-
readonly DiaSessionWrapper session = new DiaSessionWrapper(assemblyFileName, diagnosticMessageSink);
23+
readonly object disposalLock = new();
24+
bool disposed;
25+
readonly DiaSessionWrapper session = new(assemblyFileName, diagnosticMessageSink);
2126

2227
/// <inheritdoc/>
2328
public SourceInformation GetSourceInformation(
@@ -37,6 +42,14 @@ public SourceInformation GetSourceInformation(
3742
/// <inheritdoc/>
3843
public ValueTask DisposeAsync()
3944
{
45+
lock (session)
46+
{
47+
if (disposed)
48+
throw new ObjectDisposedException(nameof(VisualStudioSourceInformationProvider));
49+
50+
disposed = true;
51+
}
52+
4053
session.Dispose();
4154

4255
return default;

0 commit comments

Comments
 (0)