only consider cancellation if it doesn't complete synchronously · StackExchange/StackExchange.Redis@e1e7189 (original) (raw)

Original file line number Diff line number Diff line change
@@ -269,6 +269,7 @@ public void Dispose()
269 269 }
270 270 OnCloseEcho();
271 271 _arena.Dispose();
272 +_reusableFlushSyncTokenSource?.Dispose();
272 273 GC.SuppressFinalize(this);
273 274 }
274 275
@@ -872,31 +873,37 @@ private async ValueTask FlushAsync_Awaited(PhysicalConnection conne
872 873 }
873 874 }
874 875
876 +CancellationTokenSource _reusableFlushSyncTokenSource;
875 877 [Obsolete("this is an anti-pattern; work to reduce reliance on this is in progress")]
876 878 internal WriteResult FlushSync(bool throwOnFailure, int millisecondsTimeout)
877 879 {
878 -using (var source = new CancellationTokenSource())
880 +var cts = _reusableFlushSyncTokenSource ??= new CancellationTokenSource();
881 +var flush = FlushAsync(throwOnFailure, cts.Token);
882 +if (!flush.IsCompletedSuccessfully)
879 883 {
880 -source.CancelAfter(TimeSpan.FromMilliseconds(millisecondsTimeout));
881 -var flush = FlushAsync(throwOnFailure, source.Token);
882 -if (!flush.IsCompletedSuccessfully)
884 +// only schedule cancellation if it doesn't complete synchronously; at this point, it is doomed
885 +_reusableFlushSyncTokenSource = null;
886 +cts.CancelAfter(TimeSpan.FromMilliseconds(millisecondsTimeout));
887 +try
883 888 {
884 -try
885 -{
886 - // here lies the evil
887 - flush.AsTask().Wait();
888 - }
889 -catch (AggregateException ex)
889 +// here lies the evil
890 +flush.AsTask().Wait();
891 +}
892 +catch (AggregateException ex)
893 +{
894 +if (ex.InnerExceptions.Any(e => e is TaskCanceledException))
890 895 {
891 -if (ex.InnerExceptions.Any(e => e is TaskCanceledException))
892 -{
893 -ThrowTimeout();
894 -}
895 -throw;
896 +ThrowTimeout();
896 897 }
898 +throw;
899 +}
900 +finally
901 +{
902 +cts.Dispose();
897 903 }
898 -return flush.Result;
899 904 }
905 +return flush.Result;
906 +
900 907 void ThrowTimeout()
901 908 {
902 909 #if DEBUG