Avoid null refs in BlazorIgntior when Disposed (#14341)
* Avoid null refs in BlazorIgntior when Disposed Fixes https://github.com/aspnet/AspNetCore/issues/14257
This commit is contained in:
commit
1937c804fa
|
|
@ -27,6 +27,7 @@ Building ASP.NET Core on Windows requires:
|
||||||
```ps1
|
```ps1
|
||||||
PS> ./eng/scripts/InstallJdk.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
|
### macOS/Linux
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ namespace Ignitor
|
||||||
|
|
||||||
public Task<CapturedRenderBatch> PrepareForNextBatch(TimeSpan? timeout)
|
public Task<CapturedRenderBatch> PrepareForNextBatch(TimeSpan? timeout)
|
||||||
{
|
{
|
||||||
if (NextBatchReceived?.Completion != null)
|
if (NextBatchReceived != null && !NextBatchReceived.Disposed)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Invalid state previous task not completed");
|
throw new InvalidOperationException("Invalid state previous task not completed");
|
||||||
}
|
}
|
||||||
|
|
@ -100,7 +100,7 @@ namespace Ignitor
|
||||||
|
|
||||||
public Task<CapturedJSInteropCall> PrepareForNextJSInterop(TimeSpan? timeout)
|
public Task<CapturedJSInteropCall> PrepareForNextJSInterop(TimeSpan? timeout)
|
||||||
{
|
{
|
||||||
if (NextJSInteropReceived?.Completion != null)
|
if (NextJSInteropReceived != null && !NextJSInteropReceived.Disposed)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Invalid state previous task not completed");
|
throw new InvalidOperationException("Invalid state previous task not completed");
|
||||||
}
|
}
|
||||||
|
|
@ -112,7 +112,7 @@ namespace Ignitor
|
||||||
|
|
||||||
public Task<string> PrepareForNextDotNetInterop(TimeSpan? timeout)
|
public Task<string> PrepareForNextDotNetInterop(TimeSpan? timeout)
|
||||||
{
|
{
|
||||||
if (NextDotNetInteropCompletionReceived?.Completion != null)
|
if (NextDotNetInteropCompletionReceived != null && !NextDotNetInteropCompletionReceived.Disposed)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Invalid state previous task not completed");
|
throw new InvalidOperationException("Invalid state previous task not completed");
|
||||||
}
|
}
|
||||||
|
|
@ -124,7 +124,7 @@ namespace Ignitor
|
||||||
|
|
||||||
public Task<string> PrepareForNextCircuitError(TimeSpan? timeout)
|
public Task<string> PrepareForNextCircuitError(TimeSpan? timeout)
|
||||||
{
|
{
|
||||||
if (NextErrorReceived?.Completion != null)
|
if (NextErrorReceived != null && !NextErrorReceived.Disposed)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Invalid state previous task not completed");
|
throw new InvalidOperationException("Invalid state previous task not completed");
|
||||||
}
|
}
|
||||||
|
|
@ -136,7 +136,7 @@ namespace Ignitor
|
||||||
|
|
||||||
public Task<Exception> PrepareForNextDisconnect(TimeSpan? timeout)
|
public Task<Exception> PrepareForNextDisconnect(TimeSpan? timeout)
|
||||||
{
|
{
|
||||||
if (NextDisconnect?.Completion != null)
|
if (NextDisconnect != null && !NextDisconnect.Disposed)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Invalid state previous task not completed");
|
throw new InvalidOperationException("Invalid state previous task not completed");
|
||||||
}
|
}
|
||||||
|
|
@ -499,56 +499,6 @@ namespace Ignitor
|
||||||
return element;
|
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)
|
private string[] ReadMarkers(string content)
|
||||||
{
|
{
|
||||||
content = content.Replace("\r\n", "").Replace("\n", "");
|
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 instance = await SeleniumStandaloneServer.GetInstanceAsync(output);
|
||||||
|
|
||||||
var attempt = 0;
|
var attempt = 0;
|
||||||
var maxAttempts = 3;
|
const int maxAttempts = 3;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
@ -132,18 +132,16 @@ namespace Microsoft.AspNetCore.E2ETesting
|
||||||
|
|
||||||
return (driver, logs);
|
return (driver, logs);
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
if (attempt >= maxAttempts)
|
output.WriteLine($"Error initializing RemoteWebDriver: {ex.Message}");
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Couldn't create a Selenium remote driver client. The server is irresponsive");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attempt++;
|
attempt++;
|
||||||
|
|
||||||
} while (attempt < maxAttempts);
|
} 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 irresponsive");
|
||||||
throw new InvalidOperationException("Couldn't create a Selenium remote driver client. The server is unresponsive");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue