Avoid null refs in BlazorIgntior when Disposed
Fixes https://github.com/aspnet/AspNetCore/issues/14257
This commit is contained in:
parent
8430a30abc
commit
0e7c873d62
|
|
@ -27,6 +27,7 @@ Building ASP.NET Core on Windows requires:
|
|||
```ps1
|
||||
PS> ./eng/scripts/InstallJdk.ps1
|
||||
```
|
||||
* Chrome - Selenium-based tests require a version of Chrome to be installed. Download and install it from [https://www.google.com/chrome]
|
||||
|
||||
### macOS/Linux
|
||||
|
||||
|
|
|
|||
|
|
@ -238,6 +238,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.HttpsP
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorServerApp", "Samples\BlazorServerApp\BlazorServerApp.csproj", "{BBF37AF9-8290-4B70-8BA8-0F6017B3B620}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Ignitor", "Ignitor", "{29B5A306-7273-4649-8B04-26234D71ADBD}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -1583,8 +1585,8 @@ Global
|
|||
{DA137BD4-F7F1-4D53-855F-5EC40CEA36B0} = {2FC10057-7A0A-4E34-8302-879925BC0102}
|
||||
{0CDAB70B-71DC-43BE-ACB7-AD2EE3541FFB} = {2FC10057-7A0A-4E34-8302-879925BC0102}
|
||||
{F88118E1-6F4A-4F89-B047-5FFD2889B9F0} = {2FC10057-7A0A-4E34-8302-879925BC0102}
|
||||
{A78CE874-76B7-46FE-8009-1ED5258BA0AA} = {D6712550-0DA2-49C8-88E1-F04CAB982BF4}
|
||||
{FC2A1EB0-A116-4689-92B7-239B1DCCF4CA} = {D6712550-0DA2-49C8-88E1-F04CAB982BF4}
|
||||
{A78CE874-76B7-46FE-8009-1ED5258BA0AA} = {29B5A306-7273-4649-8B04-26234D71ADBD}
|
||||
{FC2A1EB0-A116-4689-92B7-239B1DCCF4CA} = {29B5A306-7273-4649-8B04-26234D71ADBD}
|
||||
{74D21785-2FAB-4266-B7C4-E311EC8EE0DF} = {7260DED9-22A9-4E9D-92F4-5E8A4404DEAF}
|
||||
{E4C01A3F-D3C1-4639-A6A9-930E918843DD} = {7260DED9-22A9-4E9D-92F4-5E8A4404DEAF}
|
||||
{DE297C91-B3E9-4C6F-B74D-0AF9EFEBF684} = {A27FF193-195B-4474-8E6C-840B2E339373}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ namespace Ignitor
|
|||
|
||||
private CancellableOperation<CapturedAttachComponentCall> NextAttachComponentReceived { get; set; }
|
||||
|
||||
private CancellableOperation<CapturedRenderBatch> NextBatchReceived { get; set; }
|
||||
internal CancellableOperation<CapturedRenderBatch> NextBatchReceived { get; set; }
|
||||
|
||||
private CancellableOperation<string> NextErrorReceived { get; set; }
|
||||
|
||||
|
|
@ -88,7 +88,7 @@ namespace Ignitor
|
|||
|
||||
public Task<CapturedRenderBatch> PrepareForNextBatch(TimeSpan? timeout)
|
||||
{
|
||||
if (NextBatchReceived?.Completion != null)
|
||||
if (NextBatchReceived != null && !NextBatchReceived.Disposed)
|
||||
{
|
||||
throw new InvalidOperationException("Invalid state previous task not completed");
|
||||
}
|
||||
|
|
@ -100,7 +100,7 @@ namespace Ignitor
|
|||
|
||||
public Task<CapturedJSInteropCall> PrepareForNextJSInterop(TimeSpan? timeout)
|
||||
{
|
||||
if (NextJSInteropReceived?.Completion != null)
|
||||
if (NextJSInteropReceived != null && !NextJSInteropReceived.Disposed)
|
||||
{
|
||||
throw new InvalidOperationException("Invalid state previous task not completed");
|
||||
}
|
||||
|
|
@ -112,7 +112,7 @@ namespace Ignitor
|
|||
|
||||
public Task<string> PrepareForNextDotNetInterop(TimeSpan? timeout)
|
||||
{
|
||||
if (NextDotNetInteropCompletionReceived?.Completion != null)
|
||||
if (NextDotNetInteropCompletionReceived != null && !NextDotNetInteropCompletionReceived.Disposed)
|
||||
{
|
||||
throw new InvalidOperationException("Invalid state previous task not completed");
|
||||
}
|
||||
|
|
@ -124,7 +124,7 @@ namespace Ignitor
|
|||
|
||||
public Task<string> PrepareForNextCircuitError(TimeSpan? timeout)
|
||||
{
|
||||
if (NextErrorReceived?.Completion != null)
|
||||
if (NextErrorReceived != null && !NextErrorReceived.Disposed)
|
||||
{
|
||||
throw new InvalidOperationException("Invalid state previous task not completed");
|
||||
}
|
||||
|
|
@ -136,7 +136,7 @@ namespace Ignitor
|
|||
|
||||
public Task<Exception> PrepareForNextDisconnect(TimeSpan? timeout)
|
||||
{
|
||||
if (NextDisconnect?.Completion != null)
|
||||
if (NextDisconnect != null && !NextDisconnect.Disposed)
|
||||
{
|
||||
throw new InvalidOperationException("Invalid state previous task not completed");
|
||||
}
|
||||
|
|
@ -396,7 +396,7 @@ namespace Ignitor
|
|||
NextJSInteropReceived?.Completion?.TrySetResult(null);
|
||||
}
|
||||
|
||||
private void OnRenderBatch(int id, byte[] data)
|
||||
protected virtual void OnRenderBatch(int id, byte[] data)
|
||||
{
|
||||
var capturedBatch = new CapturedRenderBatch(id, data);
|
||||
|
||||
|
|
@ -499,56 +499,6 @@ namespace Ignitor
|
|||
return element;
|
||||
}
|
||||
|
||||
private class CancellableOperation<TResult>
|
||||
{
|
||||
public CancellableOperation(TimeSpan? timeout)
|
||||
{
|
||||
Timeout = timeout;
|
||||
Initialize();
|
||||
}
|
||||
|
||||
public TimeSpan? Timeout { get; }
|
||||
|
||||
public TaskCompletionSource<TResult> Completion { get; set; }
|
||||
|
||||
public CancellationTokenSource Cancellation { get; set; }
|
||||
|
||||
public CancellationTokenRegistration CancellationRegistration { get; set; }
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
Completion = new TaskCompletionSource<TResult>(TaskContinuationOptions.RunContinuationsAsynchronously);
|
||||
Completion.Task.ContinueWith(
|
||||
(task, state) =>
|
||||
{
|
||||
var operation = (CancellableOperation<TResult>)state;
|
||||
operation.Dispose();
|
||||
},
|
||||
this,
|
||||
TaskContinuationOptions.ExecuteSynchronously); // We need to execute synchronously to clean-up before anything else continues
|
||||
if (Timeout != null && Timeout != System.Threading.Timeout.InfiniteTimeSpan && Timeout != TimeSpan.MaxValue)
|
||||
{
|
||||
Cancellation = new CancellationTokenSource(Timeout.Value);
|
||||
CancellationRegistration = Cancellation.Token.Register(
|
||||
(self) =>
|
||||
{
|
||||
var operation = (CancellableOperation<TResult>)self;
|
||||
operation.Completion.TrySetCanceled(operation.Cancellation.Token);
|
||||
operation.Cancellation.Dispose();
|
||||
operation.CancellationRegistration.Dispose();
|
||||
},
|
||||
this);
|
||||
}
|
||||
}
|
||||
|
||||
private void Dispose()
|
||||
{
|
||||
Completion = null;
|
||||
Cancellation.Dispose();
|
||||
CancellationRegistration.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private string[] ReadMarkers(string content)
|
||||
{
|
||||
content = content.Replace("\r\n", "").Replace("\n", "");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ignitor
|
||||
{
|
||||
internal class CancellableOperation<TResult>
|
||||
{
|
||||
public CancellableOperation(TimeSpan? timeout)
|
||||
{
|
||||
Timeout = timeout;
|
||||
|
||||
Completion = new TaskCompletionSource<TResult>(TaskContinuationOptions.RunContinuationsAsynchronously);
|
||||
Completion.Task.ContinueWith(
|
||||
(task, state) =>
|
||||
{
|
||||
var operation = (CancellableOperation<TResult>)state;
|
||||
operation.Dispose();
|
||||
},
|
||||
this,
|
||||
TaskContinuationOptions.ExecuteSynchronously); // We need to execute synchronously to clean-up before anything else continues
|
||||
|
||||
if (Timeout != null && Timeout != System.Threading.Timeout.InfiniteTimeSpan && Timeout != TimeSpan.MaxValue)
|
||||
{
|
||||
Cancellation = new CancellationTokenSource(Timeout.Value);
|
||||
CancellationRegistration = Cancellation.Token.Register(
|
||||
(self) =>
|
||||
{
|
||||
var operation = (CancellableOperation<TResult>)self;
|
||||
operation.Completion.TrySetCanceled(operation.Cancellation.Token);
|
||||
operation.Cancellation.Dispose();
|
||||
operation.CancellationRegistration.Dispose();
|
||||
},
|
||||
this);
|
||||
}
|
||||
}
|
||||
|
||||
public TimeSpan? Timeout { get; }
|
||||
|
||||
public TaskCompletionSource<TResult> Completion { get; }
|
||||
|
||||
public CancellationTokenSource Cancellation { get; }
|
||||
|
||||
public CancellationTokenRegistration CancellationRegistration { get; }
|
||||
|
||||
public bool Disposed { get; private set; }
|
||||
|
||||
private void Dispose()
|
||||
{
|
||||
if (Disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Disposed = true;
|
||||
Completion.TrySetCanceled(Cancellation.Token);
|
||||
Cancellation.Dispose();
|
||||
CancellationRegistration.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -108,7 +108,7 @@ namespace Microsoft.AspNetCore.E2ETesting
|
|||
var instance = await SeleniumStandaloneServer.GetInstanceAsync(output);
|
||||
|
||||
var attempt = 0;
|
||||
var maxAttempts = 3;
|
||||
const int maxAttempts = 3;
|
||||
do
|
||||
{
|
||||
try
|
||||
|
|
@ -132,18 +132,16 @@ namespace Microsoft.AspNetCore.E2ETesting
|
|||
|
||||
return (driver, logs);
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (attempt >= maxAttempts)
|
||||
{
|
||||
throw new InvalidOperationException("Couldn't create a Selenium remote driver client. The server is irresponsive");
|
||||
}
|
||||
output.WriteLine($"Error initializing RemoteWebDriver: {ex.Message}");
|
||||
}
|
||||
|
||||
attempt++;
|
||||
|
||||
} while (attempt < maxAttempts);
|
||||
|
||||
// We will never get here. Keeping the compiler happy.
|
||||
throw new InvalidOperationException("Couldn't create a Selenium remote driver client. The server is unresponsive");
|
||||
throw new InvalidOperationException("Couldn't create a Selenium remote driver client. The server is irresponsive");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue