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
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
@ -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