Skip to content

Commit

Permalink
[Blazor] Async disposable support for Blazor (#23813)
Browse files Browse the repository at this point in the history
* [Blazor] Support IAsyncDisposable in components
* Handles async disposal of components within the Blazor pipeline.
* Renders remain synchronous and don't wait for disposal to complete.
* Synchronous disposal executions remain inlined.
* Async disposal executions can trigger renders in different batches.
* Async disposals are handled individually and not grouped based on the batch they were generated on.
  • Loading branch information
javiercn authored Jul 16, 2020
1 parent b7c3b48 commit 763ccb2
Show file tree
Hide file tree
Showing 7 changed files with 461 additions and 8 deletions.
93 changes: 93 additions & 0 deletions AspNetCore.sln
Original file line number Diff line number Diff line change
Expand Up @@ -1427,6 +1427,20 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Compon
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.Web.Extensions.Tests", "src\Components\Web.Extensions\test\Microsoft.AspNetCore.Components.Web.Extensions.Tests.csproj", "{157605CB-5170-4C1A-980F-4BAE42DB60DE}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "testassets", "testassets", "{6126DCE4-9692-4EE2-B240-C65743572995}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BasicTestApp", "src\Components\test\testassets\BasicTestApp\BasicTestApp.csproj", "{46FB7E93-1294-4068-B80A-D4864F78277A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ComponentsApp.App", "src\Components\test\testassets\ComponentsApp.App\ComponentsApp.App.csproj", "{25FA84DB-EEA7-4068-8E2D-F3D48B281C16}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ComponentsApp.Server", "src\Components\test\testassets\ComponentsApp.Server\ComponentsApp.Server.csproj", "{19974360-4A63-425A-94DB-C2C940A3A97A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LazyTestContentPackage", "src\Components\test\testassets\LazyTestContentPackage\LazyTestContentPackage.csproj", "{ADF9C126-F322-4E34-AFD3-E626A4487206}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestContentPackage", "src\Components\test\testassets\TestContentPackage\TestContentPackage.csproj", "{3D3C7D9B-E356-4DC6-80B1-3F6D7F15EE31}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Components.TestServer", "src\Components\test\testassets\TestServer\Components.TestServer.csproj", "{8A59AF88-4A82-46ED-977D-D909001F8107}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -6729,6 +6743,78 @@ Global
{157605CB-5170-4C1A-980F-4BAE42DB60DE}.Release|x64.Build.0 = Release|Any CPU
{157605CB-5170-4C1A-980F-4BAE42DB60DE}.Release|x86.ActiveCfg = Release|Any CPU
{157605CB-5170-4C1A-980F-4BAE42DB60DE}.Release|x86.Build.0 = Release|Any CPU
{46FB7E93-1294-4068-B80A-D4864F78277A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{46FB7E93-1294-4068-B80A-D4864F78277A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{46FB7E93-1294-4068-B80A-D4864F78277A}.Debug|x64.ActiveCfg = Debug|Any CPU
{46FB7E93-1294-4068-B80A-D4864F78277A}.Debug|x64.Build.0 = Debug|Any CPU
{46FB7E93-1294-4068-B80A-D4864F78277A}.Debug|x86.ActiveCfg = Debug|Any CPU
{46FB7E93-1294-4068-B80A-D4864F78277A}.Debug|x86.Build.0 = Debug|Any CPU
{46FB7E93-1294-4068-B80A-D4864F78277A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{46FB7E93-1294-4068-B80A-D4864F78277A}.Release|Any CPU.Build.0 = Release|Any CPU
{46FB7E93-1294-4068-B80A-D4864F78277A}.Release|x64.ActiveCfg = Release|Any CPU
{46FB7E93-1294-4068-B80A-D4864F78277A}.Release|x64.Build.0 = Release|Any CPU
{46FB7E93-1294-4068-B80A-D4864F78277A}.Release|x86.ActiveCfg = Release|Any CPU
{46FB7E93-1294-4068-B80A-D4864F78277A}.Release|x86.Build.0 = Release|Any CPU
{25FA84DB-EEA7-4068-8E2D-F3D48B281C16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{25FA84DB-EEA7-4068-8E2D-F3D48B281C16}.Debug|Any CPU.Build.0 = Debug|Any CPU
{25FA84DB-EEA7-4068-8E2D-F3D48B281C16}.Debug|x64.ActiveCfg = Debug|Any CPU
{25FA84DB-EEA7-4068-8E2D-F3D48B281C16}.Debug|x64.Build.0 = Debug|Any CPU
{25FA84DB-EEA7-4068-8E2D-F3D48B281C16}.Debug|x86.ActiveCfg = Debug|Any CPU
{25FA84DB-EEA7-4068-8E2D-F3D48B281C16}.Debug|x86.Build.0 = Debug|Any CPU
{25FA84DB-EEA7-4068-8E2D-F3D48B281C16}.Release|Any CPU.ActiveCfg = Release|Any CPU
{25FA84DB-EEA7-4068-8E2D-F3D48B281C16}.Release|Any CPU.Build.0 = Release|Any CPU
{25FA84DB-EEA7-4068-8E2D-F3D48B281C16}.Release|x64.ActiveCfg = Release|Any CPU
{25FA84DB-EEA7-4068-8E2D-F3D48B281C16}.Release|x64.Build.0 = Release|Any CPU
{25FA84DB-EEA7-4068-8E2D-F3D48B281C16}.Release|x86.ActiveCfg = Release|Any CPU
{25FA84DB-EEA7-4068-8E2D-F3D48B281C16}.Release|x86.Build.0 = Release|Any CPU
{19974360-4A63-425A-94DB-C2C940A3A97A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{19974360-4A63-425A-94DB-C2C940A3A97A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{19974360-4A63-425A-94DB-C2C940A3A97A}.Debug|x64.ActiveCfg = Debug|Any CPU
{19974360-4A63-425A-94DB-C2C940A3A97A}.Debug|x64.Build.0 = Debug|Any CPU
{19974360-4A63-425A-94DB-C2C940A3A97A}.Debug|x86.ActiveCfg = Debug|Any CPU
{19974360-4A63-425A-94DB-C2C940A3A97A}.Debug|x86.Build.0 = Debug|Any CPU
{19974360-4A63-425A-94DB-C2C940A3A97A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{19974360-4A63-425A-94DB-C2C940A3A97A}.Release|Any CPU.Build.0 = Release|Any CPU
{19974360-4A63-425A-94DB-C2C940A3A97A}.Release|x64.ActiveCfg = Release|Any CPU
{19974360-4A63-425A-94DB-C2C940A3A97A}.Release|x64.Build.0 = Release|Any CPU
{19974360-4A63-425A-94DB-C2C940A3A97A}.Release|x86.ActiveCfg = Release|Any CPU
{19974360-4A63-425A-94DB-C2C940A3A97A}.Release|x86.Build.0 = Release|Any CPU
{ADF9C126-F322-4E34-AFD3-E626A4487206}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ADF9C126-F322-4E34-AFD3-E626A4487206}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ADF9C126-F322-4E34-AFD3-E626A4487206}.Debug|x64.ActiveCfg = Debug|Any CPU
{ADF9C126-F322-4E34-AFD3-E626A4487206}.Debug|x64.Build.0 = Debug|Any CPU
{ADF9C126-F322-4E34-AFD3-E626A4487206}.Debug|x86.ActiveCfg = Debug|Any CPU
{ADF9C126-F322-4E34-AFD3-E626A4487206}.Debug|x86.Build.0 = Debug|Any CPU
{ADF9C126-F322-4E34-AFD3-E626A4487206}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ADF9C126-F322-4E34-AFD3-E626A4487206}.Release|Any CPU.Build.0 = Release|Any CPU
{ADF9C126-F322-4E34-AFD3-E626A4487206}.Release|x64.ActiveCfg = Release|Any CPU
{ADF9C126-F322-4E34-AFD3-E626A4487206}.Release|x64.Build.0 = Release|Any CPU
{ADF9C126-F322-4E34-AFD3-E626A4487206}.Release|x86.ActiveCfg = Release|Any CPU
{ADF9C126-F322-4E34-AFD3-E626A4487206}.Release|x86.Build.0 = Release|Any CPU
{3D3C7D9B-E356-4DC6-80B1-3F6D7F15EE31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3D3C7D9B-E356-4DC6-80B1-3F6D7F15EE31}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3D3C7D9B-E356-4DC6-80B1-3F6D7F15EE31}.Debug|x64.ActiveCfg = Debug|Any CPU
{3D3C7D9B-E356-4DC6-80B1-3F6D7F15EE31}.Debug|x64.Build.0 = Debug|Any CPU
{3D3C7D9B-E356-4DC6-80B1-3F6D7F15EE31}.Debug|x86.ActiveCfg = Debug|Any CPU
{3D3C7D9B-E356-4DC6-80B1-3F6D7F15EE31}.Debug|x86.Build.0 = Debug|Any CPU
{3D3C7D9B-E356-4DC6-80B1-3F6D7F15EE31}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3D3C7D9B-E356-4DC6-80B1-3F6D7F15EE31}.Release|Any CPU.Build.0 = Release|Any CPU
{3D3C7D9B-E356-4DC6-80B1-3F6D7F15EE31}.Release|x64.ActiveCfg = Release|Any CPU
{3D3C7D9B-E356-4DC6-80B1-3F6D7F15EE31}.Release|x64.Build.0 = Release|Any CPU
{3D3C7D9B-E356-4DC6-80B1-3F6D7F15EE31}.Release|x86.ActiveCfg = Release|Any CPU
{3D3C7D9B-E356-4DC6-80B1-3F6D7F15EE31}.Release|x86.Build.0 = Release|Any CPU
{8A59AF88-4A82-46ED-977D-D909001F8107}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8A59AF88-4A82-46ED-977D-D909001F8107}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8A59AF88-4A82-46ED-977D-D909001F8107}.Debug|x64.ActiveCfg = Debug|Any CPU
{8A59AF88-4A82-46ED-977D-D909001F8107}.Debug|x64.Build.0 = Debug|Any CPU
{8A59AF88-4A82-46ED-977D-D909001F8107}.Debug|x86.ActiveCfg = Debug|Any CPU
{8A59AF88-4A82-46ED-977D-D909001F8107}.Debug|x86.Build.0 = Debug|Any CPU
{8A59AF88-4A82-46ED-977D-D909001F8107}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8A59AF88-4A82-46ED-977D-D909001F8107}.Release|Any CPU.Build.0 = Release|Any CPU
{8A59AF88-4A82-46ED-977D-D909001F8107}.Release|x64.ActiveCfg = Release|Any CPU
{8A59AF88-4A82-46ED-977D-D909001F8107}.Release|x64.Build.0 = Release|Any CPU
{8A59AF88-4A82-46ED-977D-D909001F8107}.Release|x86.ActiveCfg = Release|Any CPU
{8A59AF88-4A82-46ED-977D-D909001F8107}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -7444,6 +7530,13 @@ Global
{F71FE795-9923-461B-9809-BB1821A276D0} = {60D51C98-2CC0-40DF-B338-44154EFEE2FF}
{8294A74F-7DAA-4B69-BC56-7634D93C9693} = {F71FE795-9923-461B-9809-BB1821A276D0}
{157605CB-5170-4C1A-980F-4BAE42DB60DE} = {F71FE795-9923-461B-9809-BB1821A276D0}
{6126DCE4-9692-4EE2-B240-C65743572995} = {0508E463-0269-40C9-B5C2-3B600FB2A28B}
{46FB7E93-1294-4068-B80A-D4864F78277A} = {6126DCE4-9692-4EE2-B240-C65743572995}
{25FA84DB-EEA7-4068-8E2D-F3D48B281C16} = {6126DCE4-9692-4EE2-B240-C65743572995}
{19974360-4A63-425A-94DB-C2C940A3A97A} = {6126DCE4-9692-4EE2-B240-C65743572995}
{ADF9C126-F322-4E34-AFD3-E626A4487206} = {6126DCE4-9692-4EE2-B240-C65743572995}
{3D3C7D9B-E356-4DC6-80B1-3F6D7F15EE31} = {6126DCE4-9692-4EE2-B240-C65743572995}
{8A59AF88-4A82-46ED-977D-D909001F8107} = {6126DCE4-9692-4EE2-B240-C65743572995}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F}
Expand Down
39 changes: 36 additions & 3 deletions src/Components/Components/src/RenderTree/Renderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,7 @@ private void ProcessRenderQueue()
{
ProcessRenderQueue();
}

ComponentsProfiling.Instance.End();
}

Expand Down Expand Up @@ -634,11 +635,43 @@ private void RenderInExistingBatch(RenderQueueEntry renderQueueEntry)
var disposeComponentId = _batchBuilder.ComponentDisposalQueue.Dequeue();
var disposeComponentState = GetRequiredComponentState(disposeComponentId);
Log.DisposingComponent(_logger, disposeComponentState);
if (!disposeComponentState.TryDisposeInBatch(_batchBuilder, out var exception))
if (!(disposeComponentState.Component is IAsyncDisposable))
{
if (!disposeComponentState.TryDisposeInBatch(_batchBuilder, out var exception))
{
exceptions ??= new List<Exception>();
exceptions.Add(exception);
}
}
else
{
exceptions ??= new List<Exception>();
exceptions.Add(exception);
var result = disposeComponentState.DisposeInBatchAsync(_batchBuilder);
if (result.IsCompleted)
{
if (!result.IsCompletedSuccessfully)
{
exceptions ??= new List<Exception>();
exceptions.Add(result.Exception);
}
}
else
{
AddToPendingTasks(GetHandledAsynchronousDisposalErrorsTask(result));

async Task GetHandledAsynchronousDisposalErrorsTask(Task result)
{
try
{
await result;
}
catch (Exception e)
{
HandleException(e);
}
}
}
}

_componentStateById.Remove(disposeComponentId);
_batchBuilder.DisposedComponentIds.Append(disposeComponentId);
}
Expand Down
35 changes: 33 additions & 2 deletions src/Components/Components/src/Rendering/ComponentState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ public bool TryDisposeInBatch(RenderBatchBuilder batchBuilder, [NotNullWhen(fals
exception = ex;
}

CleanupComponentStateResources(batchBuilder);

return exception == null;
}

private void CleanupComponentStateResources(RenderBatchBuilder batchBuilder)
{
// We don't expect these things to throw.
RenderTreeDiffBuilder.DisposeFrames(batchBuilder, CurrentRenderTree.GetFrames());

Expand All @@ -110,8 +117,6 @@ public bool TryDisposeInBatch(RenderBatchBuilder batchBuilder, [NotNullWhen(fals
}

DisposeBuffers();

return exception == null;
}

// Callers expect this method to always return a faulted task.
Expand Down Expand Up @@ -222,5 +227,31 @@ private void DisposeBuffers()
((IDisposable)CurrentRenderTree).Dispose();
_latestDirectParametersSnapshot?.Dispose();
}

public Task DisposeInBatchAsync(RenderBatchBuilder batchBuilder)
{
_componentWasDisposed = true;

CleanupComponentStateResources(batchBuilder);

try
{
var result = ((IAsyncDisposable)Component).DisposeAsync();
if (result.IsCompletedSuccessfully)
{
return Task.CompletedTask;
}
else
{
// We know we are dealing with an exception that happened asynchronously, so return a task
// to the caller so that he can unwrap it.
return result.AsTask();
}
}
catch (Exception e)
{
return Task.FromException(e);
}
}
}
}
Loading

0 comments on commit 763ccb2

Please sign in to comment.