Skip to content

Commit 9dcbb52

Browse files
committed
Merge branch 'experimental/non-blocking-DisposeAsync'
2 parents dde7331 + 73d8476 commit 9dcbb52

5 files changed

Lines changed: 66 additions & 3 deletions

File tree

Main/CodeGeneration/DisposeUtility.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ internal interface IDisposeUtility
2626
string? DisposeExceptionHandlingAsyncFullyQualified { get; }
2727
string? DisposeSingularAsyncFullyQualified { get; }
2828
string? DisposeSingularAsyncSyncedFullyQualified { get; }
29+
string? ReleaseDisposeAsyncFullyQualified { get; }
2930
}
3031

3132
internal sealed class DisposeUtility : IDisposeUtility, IContainerInstance
@@ -48,6 +49,7 @@ internal sealed class DisposeUtility : IDisposeUtility, IContainerInstance
4849
private readonly string? _disposeExceptionHandlingSyncOnlyName;
4950
private readonly string? _disposeExceptionHandlingAsyncName;
5051
private readonly string _syncDisposalTriggeredExceptionName;
52+
private readonly string? _releaseDisposeAsyncName;
5153

5254
internal DisposeUtility(
5355
IReferenceGenerator referenceGenerator,
@@ -144,6 +146,12 @@ wellKnownTypes.IAsyncDisposable is not null
144146
: null;
145147

146148
_syncDisposalTriggeredExceptionName = "SyncDisposalTriggeredException";
149+
_releaseDisposeAsyncName = wellKnownTypes.IAsyncDisposable is not null && wellKnownTypes.ValueTask is not null
150+
? referenceGenerator.Generate("ReleaseDisposeAsync")
151+
: null;
152+
ReleaseDisposeAsyncFullyQualified = _releaseDisposeAsyncName is not null
153+
? $"{Constants.NamespaceForGeneratedStatics}.{ClassName}.{_releaseDisposeAsyncName}"
154+
: null;
147155
}
148156

149157
public DisposalUtilityInterfaceData DisposableRangeInterfaceData { get; }
@@ -160,6 +168,7 @@ wellKnownTypes.IAsyncDisposable is not null
160168
private string DisposeSingularFullyQualified { get; }
161169
public string? DisposeSingularAsyncFullyQualified { get; }
162170
public string? DisposeSingularAsyncSyncedFullyQualified { get; }
171+
public string? ReleaseDisposeAsyncFullyQualified { get; }
163172
public string ClassName { get; }
164173

165174
public string GenerateSingularDisposeFunctionsFile()
@@ -297,6 +306,23 @@ internal static class {{ClassName}}
297306
""");
298307
}
299308

309+
if (_wellKnownTypes.IAsyncDisposable is not null && _wellKnownTypes.ValueTask is not null)
310+
{
311+
var disposedReference = _referenceGenerator.Generate("disposed");
312+
var resolutionCounterReference = _referenceGenerator.Generate("resolutionCounter");
313+
var awaitDisposalSourceReference = _referenceGenerator.Generate("awaitDisposalSource");
314+
code.AppendLine(
315+
$$"""
316+
internal static void {{_releaseDisposeAsyncName}}(ref int {{disposedReference}}, ref readonly int {{resolutionCounterReference}}, {{_wellKnownTypes.TaskCompletionSourceOfInt.FullName()}} {{awaitDisposalSourceReference}})
317+
{
318+
if ({{disposedReference}} == 0 || {{resolutionCounterReference}} > 0)
319+
return;
320+
if ({{_wellKnownTypes.Interlocked.FullName()}}.Exchange(ref {{disposedReference}}, 2) != 2)
321+
{{awaitDisposalSourceReference}}.SetResult(0);
322+
}
323+
""");
324+
}
325+
300326
code.AppendLine(
301327
"""
302328
}

Main/CodeGeneration/Nodes/FunctionNodeGenerator.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,8 +244,13 @@ private void GenerateOneFunction(
244244
finally
245245
{
246246
{{_wellKnownTypes.Interlocked.FullName()}}.{{nameof(Interlocked.Decrement)}}(ref {{_range.ResolutionCounterReference}});
247-
}
248247
""");
248+
if (_wellKnownTypes.ValueTask is not null && _wellKnownTypes.IAsyncDisposable is not null && _disposeUtility.ReleaseDisposeAsyncFullyQualified is {} releaseDisposeAsyncFullyQualified)
249+
{
250+
code.AppendLine(
251+
$"{releaseDisposeAsyncFullyQualified}(ref {_range.DisposalHandling.DisposedFieldReference}, ref {_range.ResolutionCounterReference}, {_range.ReleaseDisposeAsyncReference});");
252+
}
253+
code.AppendLine("}");
249254
}
250255

251256
switch (_function)

Main/CodeGeneration/Nodes/RangeNodeGenerator.cs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Threading;
2+
using System.Threading.Tasks;
23
using MrMeeseeks.DIE.Configuration;
34
using MrMeeseeks.DIE.Nodes.Functions;
45
using MrMeeseeks.DIE.Nodes.Ranges;
@@ -63,10 +64,16 @@ public void Generate(StringBuilder code, ICodeGenerationVisitor visitor)
6364
code.AppendLine($"{DefaultConstructorDeclaredAccessibility}{_rangeNode.Name}() {{ }}");
6465

6566
PreGeneralContent(code, visitor);
67+
68+
if (_wellKnownTypes.ValueTask is not null && _wellKnownTypes.IAsyncDisposable is not null)
69+
{
70+
code.AppendLine(
71+
$"private readonly {_wellKnownTypes.TaskCompletionSourceOfInt.FullName()} {_rangeNode.ReleaseDisposeAsyncReference} = new {_wellKnownTypes.TaskCompletionSourceOfInt.FullName()}();");
72+
}
6673

6774
code.AppendLine(
6875
$$"""
69-
private {{_wellKnownTypes.Int32.FullName()}} {{_rangeNode.ResolutionCounterReference}};
76+
private {{_wellKnownTypes.Int32.FullName()}} {{_rangeNode.ResolutionCounterReference}} = 0;
7077
private {{_wellKnownTypes.ListOfListOfObject.FullName()}} {{_rangeNode.DisposalHandling.CollectionReference}} = new {{_wellKnownTypes.ListOfListOfObject.FullName()}}();
7178
""");
7279
foreach (var initializedInstance in _rangeNode.InitializedInstances)
@@ -151,6 +158,15 @@ void GenerateAddForDisposal(
151158
finally
152159
{
153160
{{_wellKnownTypes.Interlocked.FullName()}}.{{nameof(Interlocked.Decrement)}}(ref {{_rangeNode.ResolutionCounterReference}});
161+
162+
""");
163+
if (_wellKnownTypes.ValueTask is not null && _wellKnownTypes.IAsyncDisposable is not null && _disposeUtility.ReleaseDisposeAsyncFullyQualified is {} releaseDisposeAsyncFullyQualified)
164+
{
165+
code.AppendLine(
166+
$"{releaseDisposeAsyncFullyQualified}(ref {_rangeNode.DisposalHandling.DisposedFieldReference}, ref {_rangeNode.ResolutionCounterReference}, {_rangeNode.ReleaseDisposeAsyncReference});");
167+
}
168+
code.AppendLine(
169+
$$"""
154170
}
155171
}
156172
""");
@@ -244,8 +260,16 @@ void GenerateDisposeFunction(bool isAsync)
244260
{
245261
var {{disposalHandling.DisposedLocalReference}} = {{_wellKnownTypes.Interlocked.FullName()}}.{{nameof(Interlocked.Exchange)}}(ref {{disposalHandling.DisposedFieldReference}}, 1);
246262
if ({{disposalHandling.DisposedLocalReference}} != 0) return;
247-
{{_wellKnownTypes.SpinWait}}.{{nameof(SpinWait.SpinUntil)}}(() => {{_rangeNode.ResolutionCounterReference}} == 0);
248263
""");
264+
if (_disposeUtility.ReleaseDisposeAsyncFullyQualified is {} releaseDisposeAsyncFullyQualified)
265+
{
266+
code.AppendLine(
267+
$"{releaseDisposeAsyncFullyQualified}(ref {_rangeNode.DisposalHandling.DisposedFieldReference}, ref {_rangeNode.ResolutionCounterReference}, {_rangeNode.ReleaseDisposeAsyncReference});");
268+
}
269+
270+
code.AppendLine(isAsync && _disposeUtility.ReleaseDisposeAsyncFullyQualified is not null
271+
? $"await {_rangeNode.ReleaseDisposeAsyncReference}.{nameof(TaskCompletionSource<int>.Task)};"
272+
: $"{_wellKnownTypes.SpinWait}.{nameof(SpinWait.SpinUntil)}(() => {_rangeNode.ResolutionCounterReference} == 0);");
249273

250274
switch (_rangeNode)
251275
{

Main/Nodes/Ranges/RangeNode.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ internal interface IRangeNode : INode
2323
string? ContainerReference { get; }
2424
IEnumerable<IInitializedInstanceNode> InitializedInstances { get; }
2525
string ResolutionCounterReference { get; }
26+
string ReleaseDisposeAsyncReference { get; }
2627

2728
IFunctionCallNode BuildCreateCall(ITypeSymbol type, IFunctionNode callingFunction, ImplementationMappingConfiguration? implementationMappingConfiguration = null);
2829
IWrappedAsyncFunctionCallNode BuildAsyncCreateCall(
@@ -101,6 +102,7 @@ internal abstract class RangeNode : IRangeNode
101102

102103
public IEnumerable<IInitializedInstanceNode> InitializedInstances => InitializedInstanceNodesMap.Values;
103104
public string ResolutionCounterReference { get; }
105+
public string ReleaseDisposeAsyncReference { get; }
104106

105107
public IFunctionCallNode BuildEnumerableCall(INamedTypeSymbol type, IFunctionNode callingFunction,
106108
PassedContext passedContext) =>
@@ -278,6 +280,7 @@ internal RangeNode(
278280
}
279281

280282
ResolutionCounterReference = referenceGenerator.Generate("resolutionCounter");
283+
ReleaseDisposeAsyncReference = referenceGenerator.Generate("releaseDisposeAsync");
281284
}
282285

283286
protected abstract IScopeManager ScopeManager { get; }

Main/WellKnownTypes.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ internal sealed record WellKnownTypes(
1414
INamedTypeSymbol? ValueTask1, // .NET Standard 2.1
1515
INamedTypeSymbol Task, // .NET Standard 2.0
1616
INamedTypeSymbol Task1, // .NET Standard 2.0
17+
INamedTypeSymbol TaskCompletionSource1, // .NET Standard 2.0
18+
INamedTypeSymbol TaskCompletionSourceOfInt, // .NET Standard 2.0
1719
INamedTypeSymbol SpinWait, // .NET Standard 2.0
1820
INamedTypeSymbol Thread, // .NET Standard 2.0
1921
INamedTypeSymbol ObjectDisposedException, // .NET Standard 2.0
@@ -56,6 +58,7 @@ internal static WellKnownTypes Create(Compilation compilation)
5658
var exception = compilation.GetTypeByMetadataNameOrThrow("System.Exception");
5759
var task1 = compilation.GetTypeByMetadataNameOrThrow("System.Threading.Tasks.Task`1");
5860
var aggregateException = compilation.GetTypeByMetadataNameOrThrow("System.AggregateException");
61+
var taskCompletionSource1 = compilation.GetTypeByMetadataNameOrThrow("System.Threading.Tasks.TaskCompletionSource`1");
5962

6063
return new WellKnownTypes(
6164
IDisposable: iDisposable,
@@ -66,6 +69,8 @@ internal static WellKnownTypes Create(Compilation compilation)
6669
ValueTask1: valueTask1,
6770
Task: compilation.GetTypeByMetadataNameOrThrow("System.Threading.Tasks.Task"),
6871
Task1: task1,
72+
TaskCompletionSource1: taskCompletionSource1,
73+
TaskCompletionSourceOfInt: taskCompletionSource1.Construct(compilation.GetSpecialType(SpecialType.System_Int32)),
6974
SpinWait: compilation.GetTypeByMetadataNameOrThrow("System.Threading.SpinWait"),
7075
Thread: compilation.GetTypeByMetadataNameOrThrow("System.Threading.Thread"),
7176
ObjectDisposedException: compilation.GetTypeByMetadataNameOrThrow("System.ObjectDisposedException"),

0 commit comments

Comments
 (0)