Skip to content

Commit f07e797

Browse files
authored
[Internal]: Fixes the Observed exception (ObjectDisposedException) in GlobalEndpointManager (#5197)
Internal: Fixes the race condition where a background tasks fails and try to access a disposed object. - [] Bug fix (non-breaking change which fixes an issue)
1 parent 2baee60 commit f07e797

1 file changed

Lines changed: 55 additions & 12 deletions

File tree

Microsoft.Azure.Cosmos/src/Routing/GlobalEndpointManager.cs

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,14 @@ private async Task GetAndUpdateAccountPropertiesAsync(Uri endpoint)
339339
if (databaseAccount != null)
340340
{
341341
this.AccountProperties = databaseAccount;
342-
this.CancellationTokenSource.Cancel();
342+
try
343+
{
344+
this.CancellationTokenSource.Cancel();
345+
}
346+
catch (ObjectDisposedException)
347+
{
348+
// Ignore the exception if the cancellation token source is already disposed
349+
}
343350
}
344351
}
345352
catch (Exception e)
@@ -348,7 +355,14 @@ private async Task GetAndUpdateAccountPropertiesAsync(Uri endpoint)
348355
if (GetAccountPropertiesHelper.IsNonRetriableException(e))
349356
{
350357
DefaultTrace.TraceInformation("GlobalEndpointManager: Exception is not retriable");
351-
this.CancellationTokenSource.Cancel();
358+
try
359+
{
360+
this.CancellationTokenSource.Cancel();
361+
}
362+
catch (ObjectDisposedException)
363+
{
364+
// Ignore the exception if the cancellation token source is already disposed
365+
}
352366
this.NonRetriableException = e;
353367
}
354368
else
@@ -405,15 +419,36 @@ private static IEnumerable<Uri> GetServiceEndpoints(
405419
}
406420
}
407421
}
408-
409422
public void Dispose()
410423
{
411-
if (Interlocked.Increment(ref this.disposeCounter) == 1)
424+
// Dispose of unmanaged resources.
425+
this.Dispose(true);
426+
// Suppress finalization.
427+
GC.SuppressFinalize(this);
428+
}
429+
protected virtual void Dispose(bool disposing)
430+
{
431+
if (Interlocked.Increment(ref this.disposeCounter) != 1)
412432
{
413-
this.CancellationTokenSource?.Cancel();
414-
this.CancellationTokenSource?.Dispose();
433+
return;
434+
}
435+
436+
if (disposing)
437+
{
438+
try
439+
{
440+
this.CancellationTokenSource?.Cancel();
441+
this.CancellationTokenSource?.Dispose();
442+
}
443+
catch (ObjectDisposedException)
444+
{
445+
// Ignore exceptions during dispose
446+
}
447+
415448
}
449+
416450
}
451+
417452
}
418453

419454
public virtual Uri ResolveServiceEndpoint(DocumentServiceRequest request)
@@ -493,12 +528,20 @@ public void Dispose()
493528
{
494529
this.connectionPolicy.PreferenceChanged -= this.OnPreferenceChanged;
495530
if (!this.cancellationTokenSource.IsCancellationRequested)
496-
{
497-
// This can cause task canceled exceptions if the user disposes of the object while awaiting an async call.
498-
this.cancellationTokenSource.Cancel();
499-
// The background timer task can hit a ObjectDisposedException but it's an async background task
500-
// that is never awaited on so it will not be thrown back to the caller.
501-
this.cancellationTokenSource.Dispose();
531+
{
532+
try
533+
{
534+
// This can cause task canceled exceptions if the user disposes of the object while awaiting an async call.
535+
this.cancellationTokenSource.Cancel();
536+
// The background timer task can hit a ObjectDisposedException but it's an async background task
537+
// that is never awaited on so it will not be thrown back to the caller.
538+
this.cancellationTokenSource.Dispose();
539+
}
540+
catch (ObjectDisposedException)
541+
{
542+
// Ignore the exception if the cancellation token source is already disposed
543+
544+
}
502545
}
503546
}
504547

0 commit comments

Comments
 (0)