this is my code that I use to ping a list of IP addresses. It works fine except for today that I received a Fatal Unhandled Exception! - System.ObjectDisposedException
private static CancellationTokenSource cts = new CancellationTokenSource();
private static CancellationToken ct;
// Source per cancellation Token
ct = cts.Token;
IsRun = true;
try
{
LoopAndCheckPingAsync(AddressList.Select(a => a.IP).ToList()).ContinueWith((t) =>
{
if (t.IsFaulted)
{
Exception ex = t.Exception;
while (ex is AggregateException && ex.InnerException != null)
ex = ex.InnerException;
Global.LOG.Log("Sonar.Start() - ContinueWith Faulted:" ex.Message);
}
else
{
// Cancellation tokek
if (cts != null)
{
cts.Dispose();
}
}
});
}
catch (Exception ex)
{
Global.LOG.Log("Sonar.Start() - Exc:" ex.Message);
}
As I cannot replicate the error, my suspect is related to the Disponse method of the CancellationTokenSource. Any ideas to dispose correctly a CancellationTokenSource?
I taken the Event Viewer detail entry:
Informazioni sull'eccezione: System.ObjectDisposedException
in System.Runtime.InteropServices.SafeHandle.DangerousAddRef(Boolean ByRef)
in System.StubHelpers.StubHelpers.SafeHandleAddRef(System.Runtime.InteropServices.SafeHandle, Boolean ByRef)
in Microsoft.Win32.Win32Native.SetEvent(Microsoft.Win32.SafeHandles.SafeWaitHandle)
in System.Threading.EventWaitHandle.Set()
in System.Net.NetworkInformation.Ping.set_InAsyncCall(Boolean)
in System.Net.NetworkInformation.Ping.Finish(Boolean)
in System.Net.NetworkInformation.Ping.PingCallback(System.Object, Boolean)
in System.Threading._ThreadPoolWaitOrTimerCallback.WaitOrTimerCallback_Context(System.Object, Boolean)
in System.Threading._ThreadPoolWaitOrTimerCallback.WaitOrTimerCallback_Context_f(System.Object)
in System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
in System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
in System.Threading._ThreadPoolWaitOrTimerCallback.PerformWaitOrTimerCallback(System.Object, Boolean)
CodePudding user response:
It is not possible to tell where the error is originated from the code you have posted. Generally, you must inspect the eception message (callstack) to know where exactly the exception was triggered.
Call Dispose
once cancellation has been requested or the cancellable operations have completed. The exception is thrown when you access the mutating members of CancellationTokenSource
or its CancellationToken
instance, when the CancellationTokenSource
was exposed. Like when calling Cancel
on the disposed instance or trying to get the reference to the associated CancellationToken
after Dispose
was called. You must ensure that no code accesses the disposed instance.
You can do this by setting the CancellationTokenSource property to null when disposing and by adding a null check before accessing the CancellationTokenSource
. You must control the lifetime of the CancellationTokenSource
carefully.
The following example shows how to control the lifetime of the CancellationTokenSource
and guard against illegal referencing of the disposed instance:
private CancellationTokenSource CancellationtokenSource { get; set; }
private void CancelCancellableOperation_OnClick(object sender, EventArgs e)
{
// Check for null to avoid an ObjectDisposedException
// (or a NullReferenceException in particular) exception.
// The implemented pattern sets the property to null immediately after disposal (not thread-safe).
this.CancellationTokenSource?.Cancel();
}
// Start scope of CancellationTokenSource.
// Lifetime is managed by a try-catch-finally block and the use of
// CancellationToken.ThrowIfCancellationRequested
// to forcefully enter the try-catch-finally block on cancellation.
private async Task DoWorkAsync()
{
this.CancellationTokenSource = new CancellationTokenSource();
try
{
await CancellableOperationAsync(this.CancellationTokenSource.Token);
}
catch (OperationCanceledException)
{
// Do some cleanup or rollback.
// At this point the CancellationTokenSource is still not disposed.
}
finally
{
// Invalidate CancellationTokenSource property to raise an NullReferenceException exception
// to indicate that thet access ocurred uncontrolled and requires a fix.
// Create a local copy of the property to avoid race conditions.
var cancellationTokenSource = this.CancellationTokenSource;
this.CancellationTokenSource = null;
// Dispose after cancellation
// or cancellable operations are completed
cancellationTokenSource.Dispose();
}
}
private async Task CancellableOperationAsync(CancellationToken cancellationToken)
{
// Guarantee that CancellationTokenSource is never disposed before
// CancellationTokenSource.Cancel was called or the cancellable operation has completed
// Do something
while (true)
{
await Task.Delay(TimeSpan.FromSeconds(10));
// Add null check if you can't guarantee premature disposal
cancellationToken?.ThrowIfCancellationRequested();
}
}
CodePudding user response:
Simplest solution for your problem is not to dispose of Cancellation token source.
According to MS and some posts disposal of cancellation token source is required only if it is a Linked cancellation token source
or (here I'm not entirely sure) if there is something assigned by token's Register
method.