Selaa lähdekoodia

Adding "deep cancellation" API surface predicated by a NO_DEEP_CANCELLATION symbol.

Bart De Smet 7 vuotta sitten
vanhempi
sitoutus
d596d9f6d8
40 muutettua tiedostoa jossa 5178 lisäystä ja 14 poistoa
  1. 15 0
      Ix.NET/Source/System.Linq.Async/System/Linq/AsyncIterator.Opt.cs
  2. 5 0
      Ix.NET/Source/System.Linq.Async/System/Linq/IOrderedAsyncEnumerable.cs
  3. 112 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Aggregate.cs
  4. 34 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/All.cs
  5. 34 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Any.cs
  6. 120 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Average.Generated.cs
  7. 12 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Average.Generated.tt
  8. 351 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Average.cs
  9. 41 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Count.cs
  10. 22 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/First.cs
  11. 47 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/FirstOrDefault.cs
  12. 394 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/GroupBy.cs
  13. 132 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/GroupJoin.cs
  14. 165 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Join.cs
  15. 21 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Last.cs
  16. 56 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/LastOrDefault.cs
  17. 41 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/LongCount.cs
  18. 127 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Lookup.cs
  19. 67 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Max.Generic.cs
  20. 455 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Max.Primitive.cs
  21. 12 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Max.cs
  22. 67 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Min.Generic.cs
  23. 393 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Min.Primitive.cs
  24. 12 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Min.cs
  25. 240 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/MinMax.Generated.cs
  26. 12 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/MinMax.Generated.tt
  27. 94 8
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/OrderBy.cs
  28. 80 6
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/OrderedAsyncEnumerable.cs
  29. 285 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Select.cs
  30. 453 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/SelectMany.cs
  31. 46 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Single.cs
  32. 46 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/SingleOrDefault.cs
  33. 188 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/SkipWhile.cs
  34. 382 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Sum.Generated.cs
  35. 53 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Sum.Generated.tt
  36. 163 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/TakeWhile.cs
  37. 80 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/ToDictionary.cs
  38. 59 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/ToLookup.cs
  39. 174 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Where.cs
  40. 88 0
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Zip.cs

+ 15 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/AsyncIterator.Opt.cs

@@ -3,6 +3,7 @@
 // See the LICENSE file in the project root for more information. 
 
 using System.Collections.Generic;
+using System.Threading;
 using System.Threading.Tasks;
 
 namespace System.Linq
@@ -19,6 +20,13 @@ namespace System.Linq
             return new AsyncEnumerable.SelectEnumerableAsyncIteratorWithTask<TSource, TResult>(this, selector);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public virtual IAsyncEnumerable<TResult> Select<TResult>(Func<TSource, CancellationToken, ValueTask<TResult>> selector)
+        {
+            return new AsyncEnumerable.SelectEnumerableAsyncIteratorWithTaskAndCancellation<TSource, TResult>(this, selector);
+        }
+#endif
+
         public virtual IAsyncEnumerable<TSource> Where(Func<TSource, bool> predicate)
         {
             return new AsyncEnumerable.WhereEnumerableAsyncIterator<TSource>(this, predicate);
@@ -28,5 +36,12 @@ namespace System.Linq
         {
             return new AsyncEnumerable.WhereEnumerableAsyncIteratorWithTask<TSource>(this, predicate);
         }
+
+#if !NO_DEEP_CANCELLATION
+        public virtual IAsyncEnumerable<TSource> Where(Func<TSource, CancellationToken, ValueTask<bool>> predicate)
+        {
+            return new AsyncEnumerable.WhereEnumerableAsyncIteratorWithTaskAndCancellation<TSource>(this, predicate);
+        }
+#endif
     }
 }

+ 5 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/IOrderedAsyncEnumerable.cs

@@ -3,6 +3,7 @@
 // See the LICENSE file in the project root for more information. 
 
 using System.Collections.Generic;
+using System.Threading;
 using System.Threading.Tasks;
 
 namespace System.Linq
@@ -11,5 +12,9 @@ namespace System.Linq
     {
         IOrderedAsyncEnumerable<TElement> CreateOrderedEnumerable<TKey>(Func<TElement, TKey> keySelector, IComparer<TKey> comparer, bool descending);
         IOrderedAsyncEnumerable<TElement> CreateOrderedEnumerable<TKey>(Func<TElement, ValueTask<TKey>> keySelector, IComparer<TKey> comparer, bool descending);
+
+#if !NO_DEEP_CANCELLATION
+        IOrderedAsyncEnumerable<TElement> CreateOrderedEnumerable<TKey>(Func<TElement, CancellationToken, ValueTask<TKey>> keySelector, IComparer<TKey> comparer, bool descending);
+#endif
     }
 }

+ 112 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Aggregate.cs

@@ -50,6 +50,18 @@ namespace System.Linq
             return AggregateCore(source, accumulator, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<TSource> AggregateAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, TSource, CancellationToken, ValueTask<TSource>> accumulator, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (accumulator == null)
+                throw Error.ArgumentNull(nameof(accumulator));
+
+            return AggregateCore(source, accumulator, cancellationToken);
+        }
+#endif
+
         public static Task<TAccumulate> AggregateAsync<TSource, TAccumulate>(this IAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> accumulator)
         {
             if (source == null)
@@ -90,6 +102,18 @@ namespace System.Linq
             return AggregateCore(source, seed, accumulator, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<TAccumulate> AggregateAsync<TSource, TAccumulate>(this IAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, CancellationToken, ValueTask<TAccumulate>> accumulator, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (accumulator == null)
+                throw Error.ArgumentNull(nameof(accumulator));
+
+            return AggregateCore(source, seed, accumulator, cancellationToken);
+        }
+#endif
+
         public static Task<TResult> AggregateAsync<TSource, TAccumulate, TResult>(this IAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> accumulator, Func<TAccumulate, TResult> resultSelector)
         {
             if (source == null)
@@ -138,6 +162,20 @@ namespace System.Linq
             return AggregateCore(source, seed, accumulator, resultSelector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<TResult> AggregateAsync<TSource, TAccumulate, TResult>(this IAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, CancellationToken, ValueTask<TAccumulate>> accumulator, Func<TAccumulate, CancellationToken, ValueTask<TResult>> resultSelector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (accumulator == null)
+                throw Error.ArgumentNull(nameof(accumulator));
+            if (resultSelector == null)
+                throw Error.ArgumentNull(nameof(resultSelector));
+
+            return AggregateCore(source, seed, accumulator, resultSelector, cancellationToken);
+        }
+#endif
+
         private static async Task<TResult> AggregateCore<TSource, TAccumulate, TResult>(IAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> accumulator, Func<TAccumulate, TResult> resultSelector, CancellationToken cancellationToken)
         {
             var acc = seed;
@@ -206,6 +244,29 @@ namespace System.Linq
             return acc;
         }
 
+#if !NO_DEEP_CANCELLATION
+        private static async Task<TResult> AggregateCore<TSource, TResult>(IAsyncEnumerable<TSource> source, TResult seed, Func<TResult, TSource, CancellationToken, ValueTask<TResult>> accumulator, CancellationToken cancellationToken)
+        {
+            var acc = seed;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    acc = await accumulator(acc, e.Current, cancellationToken).ConfigureAwait(false);
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return acc;
+        }
+#endif
+
         private static async Task<TResult> AggregateCore<TSource, TAccumulate, TResult>(IAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, ValueTask<TAccumulate>> accumulator, Func<TAccumulate, ValueTask<TResult>> resultSelector, CancellationToken cancellationToken)
         {
             var acc = seed;
@@ -227,6 +288,29 @@ namespace System.Linq
             return await resultSelector(acc).ConfigureAwait(false);
         }
 
+#if !NO_DEEP_CANCELLATION
+        private static async Task<TResult> AggregateCore<TSource, TAccumulate, TResult>(IAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, CancellationToken, ValueTask<TAccumulate>> accumulator, Func<TAccumulate, CancellationToken, ValueTask<TResult>> resultSelector, CancellationToken cancellationToken)
+        {
+            var acc = seed;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    acc = await accumulator(acc, e.Current, cancellationToken).ConfigureAwait(false);
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return await resultSelector(acc, cancellationToken).ConfigureAwait(false);
+        }
+#endif
+
         private static async Task<TSource> AggregateCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, TSource, ValueTask<TSource>> accumulator, CancellationToken cancellationToken)
         {
             var e = source.GetAsyncEnumerator(cancellationToken);
@@ -252,5 +336,33 @@ namespace System.Linq
                 await e.DisposeAsync().ConfigureAwait(false);
             }
         }
+
+#if !NO_DEEP_CANCELLATION
+        private static async Task<TSource> AggregateCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, TSource, CancellationToken, ValueTask<TSource>> accumulator, CancellationToken cancellationToken)
+        {
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                if (!await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    throw Error.NoElements();
+                }
+
+                var acc = e.Current;
+
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    acc = await accumulator(acc, e.Current, cancellationToken).ConfigureAwait(false);
+                }
+
+                return acc;
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+        }
+#endif
     }
 }

+ 34 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/All.cs

@@ -50,6 +50,18 @@ namespace System.Linq
             return AllCore(source, predicate, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<bool> AllAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (predicate == null)
+                throw Error.ArgumentNull(nameof(predicate));
+
+            return AllCore(source, predicate, cancellationToken);
+        }
+#endif
+
         private static async Task<bool> AllCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, bool> predicate, CancellationToken cancellationToken)
         {
             var e = source.GetAsyncEnumerator(cancellationToken);
@@ -89,5 +101,27 @@ namespace System.Linq
 
             return true;
         }
+
+#if !NO_DEEP_CANCELLATION
+        private static async Task<bool> AllCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate, CancellationToken cancellationToken)
+        {
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    if (!await predicate(e.Current, cancellationToken).ConfigureAwait(false))
+                        return false;
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return true;
+        }
+#endif
     }
 }

+ 34 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Any.cs

@@ -66,6 +66,18 @@ namespace System.Linq
             return AnyCore(source, predicate, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<bool> AnyAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (predicate == null)
+                throw Error.ArgumentNull(nameof(predicate));
+
+            return AnyCore(source, predicate, cancellationToken);
+        }
+#endif
+
         private static async Task<bool> AnyCore<TSource>(IAsyncEnumerable<TSource> source, CancellationToken cancellationToken)
         {
             var e = source.GetAsyncEnumerator(cancellationToken);
@@ -119,5 +131,27 @@ namespace System.Linq
 
             return false;
         }
+
+#if !NO_DEEP_CANCELLATION
+        private static async Task<bool> AnyCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate, CancellationToken cancellationToken)
+        {
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    if (await predicate(e.Current, cancellationToken).ConfigureAwait(false))
+                        return true;
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return false;
+        }
+#endif
     }
 }

+ 120 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Average.Generated.cs

@@ -66,6 +66,18 @@ namespace System.Linq
             return AverageCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<double> AverageAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<int>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return AverageCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<double> AverageAsync(this IAsyncEnumerable<long> source)
         {
             if (source == null)
@@ -122,6 +134,18 @@ namespace System.Linq
             return AverageCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<double> AverageAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<long>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return AverageCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<float> AverageAsync(this IAsyncEnumerable<float> source)
         {
             if (source == null)
@@ -178,6 +202,18 @@ namespace System.Linq
             return AverageCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<float> AverageAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<float>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return AverageCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<double> AverageAsync(this IAsyncEnumerable<double> source)
         {
             if (source == null)
@@ -234,6 +270,18 @@ namespace System.Linq
             return AverageCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<double> AverageAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<double>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return AverageCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<decimal> AverageAsync(this IAsyncEnumerable<decimal> source)
         {
             if (source == null)
@@ -290,6 +338,18 @@ namespace System.Linq
             return AverageCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<decimal> AverageAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<decimal>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return AverageCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<double?> AverageAsync(this IAsyncEnumerable<int?> source)
         {
             if (source == null)
@@ -346,6 +406,18 @@ namespace System.Linq
             return AverageCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<double?> AverageAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<int?>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return AverageCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<double?> AverageAsync(this IAsyncEnumerable<long?> source)
         {
             if (source == null)
@@ -402,6 +474,18 @@ namespace System.Linq
             return AverageCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<double?> AverageAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<long?>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return AverageCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<float?> AverageAsync(this IAsyncEnumerable<float?> source)
         {
             if (source == null)
@@ -458,6 +542,18 @@ namespace System.Linq
             return AverageCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<float?> AverageAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<float?>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return AverageCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<double?> AverageAsync(this IAsyncEnumerable<double?> source)
         {
             if (source == null)
@@ -514,6 +610,18 @@ namespace System.Linq
             return AverageCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<double?> AverageAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<double?>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return AverageCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<decimal?> AverageAsync(this IAsyncEnumerable<decimal?> source)
         {
             if (source == null)
@@ -570,5 +678,17 @@ namespace System.Linq
             return AverageCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<decimal?> AverageAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<decimal?>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return AverageCore(source, selector, cancellationToken);
+        }
+#endif
+
     }
 }

+ 12 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Average.Generated.tt

@@ -91,6 +91,18 @@ foreach (var o in os)
             return AverageCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<<#=o.res#>> AverageAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<<#=o.type#>>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return AverageCore(source, selector, cancellationToken);
+        }
+#endif
+
 <#
 }
 #>

+ 351 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Average.cs

@@ -100,6 +100,38 @@ namespace System.Linq
             }
         }
 
+#if !NO_DEEP_CANCELLATION
+        private static async Task<double> AverageCore<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<int>> selector, CancellationToken cancellationToken)
+        {
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                if (!await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    throw Error.NoElements();
+                }
+
+                long sum = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                long count = 1;
+                checked
+                {
+                    while (await e.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        sum += await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                        ++count;
+                    }
+                }
+
+                return (double)sum / count;
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+        }
+#endif
+
         private static async Task<double?> AverageCore(IAsyncEnumerable<int?> source, CancellationToken cancellationToken)
         {
             var e = source.GetAsyncEnumerator(cancellationToken);
@@ -214,6 +246,46 @@ namespace System.Linq
             return null;
         }
 
+#if !NO_DEEP_CANCELLATION
+        private static async Task<double?> AverageCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<int?>> selector, CancellationToken cancellationToken)
+        {
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var v = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    if (v.HasValue)
+                    {
+                        long sum = v.GetValueOrDefault();
+                        long count = 1;
+                        checked
+                        {
+                            while (await e.MoveNextAsync().ConfigureAwait(false))
+                            {
+                                v = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                                if (v.HasValue)
+                                {
+                                    sum += v.GetValueOrDefault();
+                                    ++count;
+                                }
+                            }
+                        }
+
+                        return (double)sum / count;
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return null;
+        }
+#endif
+
         private static async Task<double> AverageCore(IAsyncEnumerable<long> source, CancellationToken cancellationToken)
         {
             var e = source.GetAsyncEnumerator(cancellationToken);
@@ -304,6 +376,38 @@ namespace System.Linq
             }
         }
 
+#if !NO_DEEP_CANCELLATION
+        private static async Task<double> AverageCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<long>> selector, CancellationToken cancellationToken)
+        {
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                if (!await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    throw Error.NoElements();
+                }
+
+                var sum = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                long count = 1;
+                checked
+                {
+                    while (await e.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        sum += await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                        ++count;
+                    }
+                }
+
+                return (double)sum / count;
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+        }
+#endif
+
         private static async Task<double?> AverageCore(IAsyncEnumerable<long?> source, CancellationToken cancellationToken)
         {
             var e = source.GetAsyncEnumerator(cancellationToken);
@@ -418,6 +522,46 @@ namespace System.Linq
             return null;
         }
 
+#if !NO_DEEP_CANCELLATION
+        private static async Task<double?> AverageCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<long?>> selector, CancellationToken cancellationToken)
+        {
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var v = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    if (v.HasValue)
+                    {
+                        var sum = v.GetValueOrDefault();
+                        long count = 1;
+                        checked
+                        {
+                            while (await e.MoveNextAsync().ConfigureAwait(false))
+                            {
+                                v = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                                if (v.HasValue)
+                                {
+                                    sum += v.GetValueOrDefault();
+                                    ++count;
+                                }
+                            }
+                        }
+
+                        return (double)sum / count;
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return null;
+        }
+#endif
+
         private static async Task<double> AverageCore(IAsyncEnumerable<double> source, CancellationToken cancellationToken)
         {
             var e = source.GetAsyncEnumerator(cancellationToken);
@@ -508,6 +652,38 @@ namespace System.Linq
             }
         }
 
+#if !NO_DEEP_CANCELLATION
+        private static async Task<double> AverageCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<double>> selector, CancellationToken cancellationToken)
+        {
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                if (!await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    throw Error.NoElements();
+                }
+
+                var sum = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                long count = 1;
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    // There is an opportunity to short-circuit here, in that if e.Current is
+                    // ever NaN then the result will always be NaN. Assuming that this case is
+                    // rare enough that not checking is the better approach generally.
+                    sum += await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    ++count;
+                }
+
+                return sum / count;
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+        }
+#endif
+
         private static async Task<double?> AverageCore(IAsyncEnumerable<double?> source, CancellationToken cancellationToken)
         {
             var e = source.GetAsyncEnumerator(cancellationToken);
@@ -622,6 +798,46 @@ namespace System.Linq
             return null;
         }
 
+#if !NO_DEEP_CANCELLATION
+        private static async Task<double?> AverageCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<double?>> selector, CancellationToken cancellationToken)
+        {
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var v = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    if (v.HasValue)
+                    {
+                        var sum = v.GetValueOrDefault();
+                        long count = 1;
+                        checked
+                        {
+                            while (await e.MoveNextAsync().ConfigureAwait(false))
+                            {
+                                v = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                                if (v.HasValue)
+                                {
+                                    sum += v.GetValueOrDefault();
+                                    ++count;
+                                }
+                            }
+                        }
+
+                        return sum / count;
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return null;
+        }
+#endif
+
         private static async Task<float> AverageCore(IAsyncEnumerable<float> source, CancellationToken cancellationToken)
         {
             var e = source.GetAsyncEnumerator(cancellationToken);
@@ -703,6 +919,35 @@ namespace System.Linq
             }
         }
 
+#if !NO_DEEP_CANCELLATION
+        private static async Task<float> AverageCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<float>> selector, CancellationToken cancellationToken)
+        {
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                if (!await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    throw Error.NoElements();
+                }
+
+                double sum = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                long count = 1;
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    sum += await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    ++count;
+                }
+
+                return (float)(sum / count);
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+        }
+#endif
+
         private static async Task<float?> AverageCore(IAsyncEnumerable<float?> source, CancellationToken cancellationToken)
         {
             var e = source.GetAsyncEnumerator(cancellationToken);
@@ -817,6 +1062,46 @@ namespace System.Linq
             return null;
         }
 
+#if !NO_DEEP_CANCELLATION
+        private static async Task<float?> AverageCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<float?>> selector, CancellationToken cancellationToken)
+        {
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var v = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    if (v.HasValue)
+                    {
+                        double sum = v.GetValueOrDefault();
+                        long count = 1;
+                        checked
+                        {
+                            while (await e.MoveNextAsync().ConfigureAwait(false))
+                            {
+                                v = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                                if (v.HasValue)
+                                {
+                                    sum += v.GetValueOrDefault();
+                                    ++count;
+                                }
+                            }
+                        }
+
+                        return (float)(sum / count);
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return null;
+        }
+#endif
+
         private static async Task<decimal> AverageCore(IAsyncEnumerable<decimal> source, CancellationToken cancellationToken)
         {
             var e = source.GetAsyncEnumerator(cancellationToken);
@@ -898,6 +1183,35 @@ namespace System.Linq
             }
         }
 
+#if !NO_DEEP_CANCELLATION
+        private static async Task<decimal> AverageCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<decimal>> selector, CancellationToken cancellationToken)
+        {
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                if (!await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    throw Error.NoElements();
+                }
+
+                var sum = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                long count = 1;
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    sum += await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    ++count;
+                }
+
+                return sum / count;
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+        }
+#endif
+
         private static async Task<decimal?> AverageCore(IAsyncEnumerable<decimal?> source, CancellationToken cancellationToken)
         {
             var e = source.GetAsyncEnumerator(cancellationToken);
@@ -1002,5 +1316,42 @@ namespace System.Linq
 
             return null;
         }
+
+#if !NO_DEEP_CANCELLATION
+        private static async Task<decimal?> AverageCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<decimal?>> selector, CancellationToken cancellationToken)
+        {
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var v = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    if (v.HasValue)
+                    {
+                        var sum = v.GetValueOrDefault();
+                        long count = 1;
+                        while (await e.MoveNextAsync().ConfigureAwait(false))
+                        {
+                            v = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                            if (v.HasValue)
+                            {
+                                sum += v.GetValueOrDefault();
+                                ++count;
+                            }
+                        }
+
+                        return sum / count;
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return null;
+        }
+#endif
     }
 }

+ 41 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Count.cs

@@ -67,6 +67,18 @@ namespace System.Linq
             return CountCore(source, predicate, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<int> CountAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (predicate == null)
+                throw Error.ArgumentNull(nameof(predicate));
+
+            return CountCore(source, predicate, cancellationToken);
+        }
+#endif
+
         private static Task<int> CountCore<TSource>(IAsyncEnumerable<TSource> source, CancellationToken cancellationToken)
         {
             switch (source)
@@ -159,5 +171,34 @@ namespace System.Linq
 
             return count;
         }
+
+#if !NO_DEEP_CANCELLATION
+        private static async Task<int> CountCore<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate, CancellationToken cancellationToken)
+        {
+            var count = 0;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    if (await predicate(e.Current, cancellationToken).ConfigureAwait(false))
+                    {
+                        checked
+                        {
+                            count++;
+                        }
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return count;
+        }
+#endif
     }
 }

+ 22 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/First.cs

@@ -66,6 +66,18 @@ namespace System.Linq
             return FirstCore(source, predicate, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<TSource> FirstAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (predicate == null)
+                throw Error.ArgumentNull(nameof(predicate));
+
+            return FirstCore(source, predicate, cancellationToken);
+        }
+#endif
+
         private static async Task<TSource> FirstCore<TSource>(IAsyncEnumerable<TSource> source, CancellationToken cancellationToken)
         {
             var first = await TryGetFirst(source, cancellationToken).ConfigureAwait(false);
@@ -86,5 +98,15 @@ namespace System.Linq
 
             return first.HasValue ? first.Value : throw Error.NoElements();
         }
+
+#if !NO_DEEP_CANCELLATION
+
+        private static async Task<TSource> FirstCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate, CancellationToken cancellationToken)
+        {
+            var first = await TryGetFirst(source, predicate, cancellationToken).ConfigureAwait(false);
+
+            return first.HasValue ? first.Value : throw Error.NoElements();
+        }
+#endif
     }
 }

+ 47 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/FirstOrDefault.cs

@@ -66,6 +66,18 @@ namespace System.Linq
             return FirstOrDefaultCore(source, predicate, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<TSource> FirstOrDefaultAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (predicate == null)
+                throw Error.ArgumentNull(nameof(predicate));
+
+            return FirstOrDefaultCore(source, predicate, cancellationToken);
+        }
+#endif
+
         private static async Task<TSource> FirstOrDefaultCore<TSource>(IAsyncEnumerable<TSource> source, CancellationToken cancellationToken)
         {
             var first = await TryGetFirst(source, cancellationToken).ConfigureAwait(false);
@@ -87,6 +99,15 @@ namespace System.Linq
             return first.HasValue ? first.Value : default;
         }
 
+#if !NO_DEEP_CANCELLATION
+        private static async Task<TSource> FirstOrDefaultCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate, CancellationToken cancellationToken)
+        {
+            var first = await TryGetFirst(source, predicate, cancellationToken).ConfigureAwait(false);
+
+            return first.HasValue ? first.Value : default;
+        }
+#endif
+
         private static ValueTask<Maybe<TSource>> TryGetFirst<TSource>(IAsyncEnumerable<TSource> source, CancellationToken cancellationToken)
         {
             if (source is IList<TSource> list)
@@ -174,5 +195,31 @@ namespace System.Linq
 
             return new Maybe<TSource>();
         }
+
+#if !NO_DEEP_CANCELLATION
+        private static async Task<Maybe<TSource>> TryGetFirst<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate, CancellationToken cancellationToken)
+        {
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var value = e.Current;
+
+                    if (await predicate(value, cancellationToken).ConfigureAwait(false))
+                    {
+                        return new Maybe<TSource>(value);
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return new Maybe<TSource>();
+        }
+#endif
     }
 }

+ 394 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/GroupBy.cs

@@ -41,6 +41,18 @@ namespace System.Linq
             return new GroupedAsyncEnumerableWithTask<TSource, TKey>(source, keySelector, comparer: null);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IAsyncEnumerable<IAsyncGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (keySelector == null)
+                throw Error.ArgumentNull(nameof(keySelector));
+
+            return new GroupedAsyncEnumerableWithTaskAndCancellation<TSource, TKey>(source, keySelector, comparer: null);
+        }
+#endif
+
         public static IAsyncEnumerable<IAsyncGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<TKey>> keySelector, IEqualityComparer<TKey> comparer)
         {
             if (source == null)
@@ -51,6 +63,18 @@ namespace System.Linq
             return new GroupedAsyncEnumerableWithTask<TSource, TKey>(source, keySelector, comparer);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IAsyncEnumerable<IAsyncGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, IEqualityComparer<TKey> comparer)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (keySelector == null)
+                throw Error.ArgumentNull(nameof(keySelector));
+
+            return new GroupedAsyncEnumerableWithTaskAndCancellation<TSource, TKey>(source, keySelector, comparer);
+        }
+#endif
+
         public static IAsyncEnumerable<IAsyncGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector)
         {
             if (source == null)
@@ -87,6 +111,20 @@ namespace System.Linq
             return new GroupedAsyncEnumerableWithTask<TSource, TKey, TElement>(source, keySelector, elementSelector, comparer: null);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IAsyncEnumerable<IAsyncGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TSource, CancellationToken, ValueTask<TElement>> elementSelector)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (keySelector == null)
+                throw Error.ArgumentNull(nameof(keySelector));
+            if (elementSelector == null)
+                throw Error.ArgumentNull(nameof(elementSelector));
+
+            return new GroupedAsyncEnumerableWithTaskAndCancellation<TSource, TKey, TElement>(source, keySelector, elementSelector, comparer: null);
+        }
+#endif
+
         public static IAsyncEnumerable<IAsyncGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<TKey>> keySelector, Func<TSource, ValueTask<TElement>> elementSelector, IEqualityComparer<TKey> comparer)
         {
             if (source == null)
@@ -99,6 +137,20 @@ namespace System.Linq
             return new GroupedAsyncEnumerableWithTask<TSource, TKey, TElement>(source, keySelector, elementSelector, comparer);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IAsyncEnumerable<IAsyncGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TSource, CancellationToken, ValueTask<TElement>> elementSelector, IEqualityComparer<TKey> comparer)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (keySelector == null)
+                throw Error.ArgumentNull(nameof(keySelector));
+            if (elementSelector == null)
+                throw Error.ArgumentNull(nameof(elementSelector));
+
+            return new GroupedAsyncEnumerableWithTaskAndCancellation<TSource, TKey, TElement>(source, keySelector, elementSelector, comparer);
+        }
+#endif
+
         public static IAsyncEnumerable<TResult> GroupBy<TSource, TKey, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TKey, IAsyncEnumerable<TSource>, TResult> resultSelector)
         {
             if (source == null)
@@ -135,6 +187,20 @@ namespace System.Linq
             return new GroupedResultAsyncEnumerableWithTask<TSource, TKey, TResult>(source, keySelector, resultSelector, comparer: null);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IAsyncEnumerable<TResult> GroupBy<TSource, TKey, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TKey, IAsyncEnumerable<TSource>, CancellationToken, ValueTask<TResult>> resultSelector)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (keySelector == null)
+                throw Error.ArgumentNull(nameof(keySelector));
+            if (resultSelector == null)
+                throw Error.ArgumentNull(nameof(resultSelector));
+
+            return new GroupedResultAsyncEnumerableWithTaskAndCancellation<TSource, TKey, TResult>(source, keySelector, resultSelector, comparer: null);
+        }
+#endif
+
         public static IAsyncEnumerable<TResult> GroupBy<TSource, TKey, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<TKey>> keySelector, Func<TKey, IAsyncEnumerable<TSource>, ValueTask<TResult>> resultSelector, IEqualityComparer<TKey> comparer)
         {
             if (source == null)
@@ -147,6 +213,20 @@ namespace System.Linq
             return new GroupedResultAsyncEnumerableWithTask<TSource, TKey, TResult>(source, keySelector, resultSelector, comparer);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IAsyncEnumerable<TResult> GroupBy<TSource, TKey, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TKey, IAsyncEnumerable<TSource>, CancellationToken, ValueTask<TResult>> resultSelector, IEqualityComparer<TKey> comparer)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (keySelector == null)
+                throw Error.ArgumentNull(nameof(keySelector));
+            if (resultSelector == null)
+                throw Error.ArgumentNull(nameof(resultSelector));
+
+            return new GroupedResultAsyncEnumerableWithTaskAndCancellation<TSource, TKey, TResult>(source, keySelector, resultSelector, comparer);
+        }
+#endif
+
         public static IAsyncEnumerable<TResult> GroupBy<TSource, TKey, TElement, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<TKey, IAsyncEnumerable<TElement>, TResult> resultSelector)
         {
             if (source == null)
@@ -189,6 +269,22 @@ namespace System.Linq
             return source.GroupBy<TSource, TKey, TElement>(keySelector, elementSelector, comparer: null).Select(g => resultSelector(g.Key, g));
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IAsyncEnumerable<TResult> GroupBy<TSource, TKey, TElement, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TSource, CancellationToken, ValueTask<TElement>> elementSelector, Func<TKey, IAsyncEnumerable<TElement>, CancellationToken, ValueTask<TResult>> resultSelector)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (keySelector == null)
+                throw Error.ArgumentNull(nameof(keySelector));
+            if (elementSelector == null)
+                throw Error.ArgumentNull(nameof(elementSelector));
+            if (resultSelector == null)
+                throw Error.ArgumentNull(nameof(resultSelector));
+
+            return source.GroupBy(keySelector, elementSelector, comparer: null).Select((g, ct) => resultSelector(g.Key, g, ct));
+        }
+#endif
+
         public static IAsyncEnumerable<TResult> GroupBy<TSource, TKey, TElement, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<TKey>> keySelector, Func<TSource, ValueTask<TElement>> elementSelector, Func<TKey, IAsyncEnumerable<TElement>, ValueTask<TResult>> resultSelector, IEqualityComparer<TKey> comparer)
         {
             if (source == null)
@@ -203,6 +299,22 @@ namespace System.Linq
             return source.GroupBy(keySelector, elementSelector, comparer).Select(g => resultSelector(g.Key, g));
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IAsyncEnumerable<TResult> GroupBy<TSource, TKey, TElement, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TSource, CancellationToken, ValueTask<TElement>> elementSelector, Func<TKey, IAsyncEnumerable<TElement>, CancellationToken, ValueTask<TResult>> resultSelector, IEqualityComparer<TKey> comparer)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (keySelector == null)
+                throw Error.ArgumentNull(nameof(keySelector));
+            if (elementSelector == null)
+                throw Error.ArgumentNull(nameof(elementSelector));
+            if (resultSelector == null)
+                throw Error.ArgumentNull(nameof(resultSelector));
+
+            return source.GroupBy(keySelector, elementSelector, comparer).Select((g, ct) => resultSelector(g.Key, g, ct));
+        }
+#endif
+
         internal sealed class GroupedResultAsyncEnumerable<TSource, TKey, TResult> : AsyncIterator<TResult>, IAsyncIListProvider<TResult>
         {
             private readonly IAsyncEnumerable<TSource> _source;
@@ -389,6 +501,101 @@ namespace System.Linq
             }
         }
 
+#if !NO_DEEP_CANCELLATION
+        internal sealed class GroupedResultAsyncEnumerableWithTaskAndCancellation<TSource, TKey, TResult> : AsyncIterator<TResult>, IAsyncIListProvider<TResult>
+        {
+            private readonly IAsyncEnumerable<TSource> _source;
+            private readonly Func<TSource, CancellationToken, ValueTask<TKey>> _keySelector;
+            private readonly Func<TKey, IAsyncEnumerable<TSource>, CancellationToken, ValueTask<TResult>> _resultSelector;
+            private readonly IEqualityComparer<TKey> _comparer;
+
+            private Internal.LookupWithTask<TKey, TSource> _lookup;
+            private IAsyncEnumerator<TResult> _enumerator;
+
+            public GroupedResultAsyncEnumerableWithTaskAndCancellation(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TKey, IAsyncEnumerable<TSource>, CancellationToken, ValueTask<TResult>> resultSelector, IEqualityComparer<TKey> comparer)
+            {
+                Debug.Assert(source != null);
+                Debug.Assert(keySelector != null);
+                Debug.Assert(resultSelector != null);
+
+                _source = source;
+                _keySelector = keySelector;
+                _resultSelector = resultSelector;
+                _comparer = comparer;
+            }
+
+            public override AsyncIteratorBase<TResult> Clone()
+            {
+                return new GroupedResultAsyncEnumerableWithTaskAndCancellation<TSource, TKey, TResult>(_source, _keySelector, _resultSelector, _comparer);
+            }
+
+            public override async ValueTask DisposeAsync()
+            {
+                if (_enumerator != null)
+                {
+                    await _enumerator.DisposeAsync().ConfigureAwait(false);
+                    _enumerator = null;
+                    _lookup = null;
+                }
+
+                await base.DisposeAsync().ConfigureAwait(false);
+            }
+
+            protected override async ValueTask<bool> MoveNextCore()
+            {
+                switch (_state)
+                {
+                    case AsyncIteratorState.Allocated:
+                        _lookup = await Internal.LookupWithTask<TKey, TSource>.CreateAsync(_source, _keySelector, _comparer, _cancellationToken).ConfigureAwait(false);
+                        _enumerator = _lookup.Select(async g => await _resultSelector(g.Key, g, _cancellationToken).ConfigureAwait(false)).GetAsyncEnumerator(_cancellationToken);
+                        _state = AsyncIteratorState.Iterating;
+                        goto case AsyncIteratorState.Iterating;
+
+                    case AsyncIteratorState.Iterating:
+                        if (await _enumerator.MoveNextAsync().ConfigureAwait(false))
+                        {
+                            _current = _enumerator.Current;
+                            return true;
+                        }
+
+                        await DisposeAsync().ConfigureAwait(false);
+                        break;
+                }
+
+                return false;
+            }
+
+            public async Task<TResult[]> ToArrayAsync(CancellationToken cancellationToken)
+            {
+                var l = await Internal.LookupWithTask<TKey, TSource>.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false);
+                return await l.ToArray(_resultSelector, cancellationToken).ConfigureAwait(false);
+            }
+
+            public async Task<List<TResult>> ToListAsync(CancellationToken cancellationToken)
+            {
+                var l = await Internal.LookupWithTask<TKey, TSource>.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false);
+                return await l.ToList(_resultSelector, cancellationToken).ConfigureAwait(false);
+            }
+
+            public Task<int> GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
+            {
+                if (onlyIfCheap)
+                {
+                    return TaskExt.MinusOne;
+                }
+
+                return Core();
+
+                async Task<int> Core()
+                {
+                    var l = await Internal.LookupWithTask<TKey, TSource>.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false);
+
+                    return l.Count;
+                }
+            }
+        }
+#endif
+
         internal sealed class GroupedAsyncEnumerable<TSource, TKey, TElement> : AsyncIterator<IAsyncGrouping<TKey, TElement>>, IAsyncIListProvider<IAsyncGrouping<TKey, TElement>>
         {
             private readonly IAsyncEnumerable<TSource> _source;
@@ -575,6 +782,101 @@ namespace System.Linq
             }
         }
 
+#if !NO_DEEP_CANCELLATION
+        internal sealed class GroupedAsyncEnumerableWithTaskAndCancellation<TSource, TKey, TElement> : AsyncIterator<IAsyncGrouping<TKey, TElement>>, IAsyncIListProvider<IAsyncGrouping<TKey, TElement>>
+        {
+            private readonly IAsyncEnumerable<TSource> _source;
+            private readonly Func<TSource, CancellationToken, ValueTask<TKey>> _keySelector;
+            private readonly Func<TSource, CancellationToken, ValueTask<TElement>> _elementSelector;
+            private readonly IEqualityComparer<TKey> _comparer;
+
+            private Internal.LookupWithTask<TKey, TElement> _lookup;
+            private IEnumerator<IGrouping<TKey, TElement>> _enumerator;
+
+            public GroupedAsyncEnumerableWithTaskAndCancellation(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TSource, CancellationToken, ValueTask<TElement>> elementSelector, IEqualityComparer<TKey> comparer)
+            {
+                Debug.Assert(source != null);
+                Debug.Assert(keySelector != null);
+                Debug.Assert(elementSelector != null);
+
+                _source = source;
+                _keySelector = keySelector;
+                _elementSelector = elementSelector;
+                _comparer = comparer;
+            }
+
+            public override AsyncIteratorBase<IAsyncGrouping<TKey, TElement>> Clone()
+            {
+                return new GroupedAsyncEnumerableWithTaskAndCancellation<TSource, TKey, TElement>(_source, _keySelector, _elementSelector, _comparer);
+            }
+
+            public override async ValueTask DisposeAsync()
+            {
+                if (_enumerator != null)
+                {
+                    _enumerator.Dispose();
+                    _enumerator = null;
+                    _lookup = null;
+                }
+
+                await base.DisposeAsync().ConfigureAwait(false);
+            }
+
+            protected override async ValueTask<bool> MoveNextCore()
+            {
+                switch (_state)
+                {
+                    case AsyncIteratorState.Allocated:
+                        _lookup = await Internal.LookupWithTask<TKey, TElement>.CreateAsync(_source, _keySelector, _elementSelector, _comparer, _cancellationToken).ConfigureAwait(false);
+                        _enumerator = _lookup.GetEnumerator();
+                        _state = AsyncIteratorState.Iterating;
+                        goto case AsyncIteratorState.Iterating;
+
+                    case AsyncIteratorState.Iterating:
+                        if (_enumerator.MoveNext())
+                        {
+                            _current = (IAsyncGrouping<TKey, TElement>)_enumerator.Current;
+                            return true;
+                        }
+
+                        await DisposeAsync().ConfigureAwait(false);
+                        break;
+                }
+
+                return false;
+            }
+
+            public async Task<IAsyncGrouping<TKey, TElement>[]> ToArrayAsync(CancellationToken cancellationToken)
+            {
+                IAsyncIListProvider<IAsyncGrouping<TKey, TElement>> l = await Internal.LookupWithTask<TKey, TElement>.CreateAsync(_source, _keySelector, _elementSelector, _comparer, cancellationToken).ConfigureAwait(false);
+                return await l.ToArrayAsync(cancellationToken).ConfigureAwait(false);
+            }
+
+            public async Task<List<IAsyncGrouping<TKey, TElement>>> ToListAsync(CancellationToken cancellationToken)
+            {
+                IAsyncIListProvider<IAsyncGrouping<TKey, TElement>> l = await Internal.LookupWithTask<TKey, TElement>.CreateAsync(_source, _keySelector, _elementSelector, _comparer, cancellationToken).ConfigureAwait(false);
+                return await l.ToListAsync(cancellationToken).ConfigureAwait(false);
+            }
+
+            public Task<int> GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
+            {
+                if (onlyIfCheap)
+                {
+                    return TaskExt.MinusOne;
+                }
+
+                return Core();
+
+                async Task<int> Core()
+                {
+                    var l = await Internal.LookupWithTask<TKey, TElement>.CreateAsync(_source, _keySelector, _elementSelector, _comparer, cancellationToken).ConfigureAwait(false);
+
+                    return l.Count;
+                }
+            }
+        }
+#endif
+
         internal sealed class GroupedAsyncEnumerable<TSource, TKey> : AsyncIterator<IAsyncGrouping<TKey, TSource>>, IAsyncIListProvider<IAsyncGrouping<TKey, TSource>>
         {
             private readonly IAsyncEnumerable<TSource> _source;
@@ -754,5 +1056,97 @@ namespace System.Linq
                 }
             }
         }
+
+#if !NO_DEEP_CANCELLATION
+        internal sealed class GroupedAsyncEnumerableWithTaskAndCancellation<TSource, TKey> : AsyncIterator<IAsyncGrouping<TKey, TSource>>, IAsyncIListProvider<IAsyncGrouping<TKey, TSource>>
+        {
+            private readonly IAsyncEnumerable<TSource> _source;
+            private readonly Func<TSource, CancellationToken, ValueTask<TKey>> _keySelector;
+            private readonly IEqualityComparer<TKey> _comparer;
+
+            private Internal.LookupWithTask<TKey, TSource> _lookup;
+            private IEnumerator<IGrouping<TKey, TSource>> _enumerator;
+
+            public GroupedAsyncEnumerableWithTaskAndCancellation(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, IEqualityComparer<TKey> comparer)
+            {
+                Debug.Assert(source != null);
+                Debug.Assert(keySelector != null);
+
+                _source = source;
+                _keySelector = keySelector;
+                _comparer = comparer;
+            }
+
+            public override AsyncIteratorBase<IAsyncGrouping<TKey, TSource>> Clone()
+            {
+                return new GroupedAsyncEnumerableWithTaskAndCancellation<TSource, TKey>(_source, _keySelector, _comparer);
+            }
+
+            public override async ValueTask DisposeAsync()
+            {
+                if (_enumerator != null)
+                {
+                    _enumerator.Dispose();
+                    _enumerator = null;
+                    _lookup = null;
+                }
+
+                await base.DisposeAsync().ConfigureAwait(false);
+            }
+
+            protected override async ValueTask<bool> MoveNextCore()
+            {
+                switch (_state)
+                {
+                    case AsyncIteratorState.Allocated:
+                        _lookup = await Internal.LookupWithTask<TKey, TSource>.CreateAsync(_source, _keySelector, _comparer, _cancellationToken).ConfigureAwait(false);
+                        _enumerator = _lookup.GetEnumerator();
+                        _state = AsyncIteratorState.Iterating;
+                        goto case AsyncIteratorState.Iterating;
+
+                    case AsyncIteratorState.Iterating:
+                        if (_enumerator.MoveNext())
+                        {
+                            _current = (IAsyncGrouping<TKey, TSource>)_enumerator.Current;
+                            return true;
+                        }
+
+                        await DisposeAsync().ConfigureAwait(false);
+                        break;
+                }
+
+                return false;
+            }
+
+            public async Task<IAsyncGrouping<TKey, TSource>[]> ToArrayAsync(CancellationToken cancellationToken)
+            {
+                IAsyncIListProvider<IAsyncGrouping<TKey, TSource>> l = await Internal.LookupWithTask<TKey, TSource>.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false);
+                return await l.ToArrayAsync(cancellationToken).ConfigureAwait(false);
+            }
+
+            public async Task<List<IAsyncGrouping<TKey, TSource>>> ToListAsync(CancellationToken cancellationToken)
+            {
+                IAsyncIListProvider<IAsyncGrouping<TKey, TSource>> l = await Internal.LookupWithTask<TKey, TSource>.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false);
+                return await l.ToListAsync(cancellationToken).ConfigureAwait(false);
+            }
+
+            public Task<int> GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
+            {
+                if (onlyIfCheap)
+                {
+                    return TaskExt.MinusOne;
+                }
+
+                return Core();
+
+                async Task<int> Core()
+                {
+                    var l = await Internal.LookupWithTask<TKey, TSource>.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false);
+
+                    return l.Count;
+                }
+            }
+        }
+#endif
     }
 }

+ 132 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/GroupJoin.cs

@@ -58,6 +58,24 @@ namespace System.Linq
             return new GroupJoinAsyncEnumerableWithTask<TOuter, TInner, TKey, TResult>(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer: null);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IAsyncEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(this IAsyncEnumerable<TOuter> outer, IAsyncEnumerable<TInner> inner, Func<TOuter, CancellationToken, ValueTask<TKey>> outerKeySelector, Func<TInner, CancellationToken, ValueTask<TKey>> innerKeySelector, Func<TOuter, IAsyncEnumerable<TInner>, CancellationToken, ValueTask<TResult>> resultSelector)
+        {
+            if (outer == null)
+                throw Error.ArgumentNull(nameof(outer));
+            if (inner == null)
+                throw Error.ArgumentNull(nameof(inner));
+            if (outerKeySelector == null)
+                throw Error.ArgumentNull(nameof(outerKeySelector));
+            if (innerKeySelector == null)
+                throw Error.ArgumentNull(nameof(innerKeySelector));
+            if (resultSelector == null)
+                throw Error.ArgumentNull(nameof(resultSelector));
+
+            return new GroupJoinAsyncEnumerableWithTaskAndCancellation<TOuter, TInner, TKey, TResult>(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer: null);
+        }
+#endif
+
         public static IAsyncEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(this IAsyncEnumerable<TOuter> outer, IAsyncEnumerable<TInner> inner, Func<TOuter, ValueTask<TKey>> outerKeySelector, Func<TInner, ValueTask<TKey>> innerKeySelector, Func<TOuter, IAsyncEnumerable<TInner>, ValueTask<TResult>> resultSelector, IEqualityComparer<TKey> comparer)
         {
             if (outer == null)
@@ -74,6 +92,24 @@ namespace System.Linq
             return new GroupJoinAsyncEnumerableWithTask<TOuter, TInner, TKey, TResult>(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IAsyncEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(this IAsyncEnumerable<TOuter> outer, IAsyncEnumerable<TInner> inner, Func<TOuter, CancellationToken, ValueTask<TKey>> outerKeySelector, Func<TInner, CancellationToken, ValueTask<TKey>> innerKeySelector, Func<TOuter, IAsyncEnumerable<TInner>, CancellationToken, ValueTask<TResult>> resultSelector, IEqualityComparer<TKey> comparer)
+        {
+            if (outer == null)
+                throw Error.ArgumentNull(nameof(outer));
+            if (inner == null)
+                throw Error.ArgumentNull(nameof(inner));
+            if (outerKeySelector == null)
+                throw Error.ArgumentNull(nameof(outerKeySelector));
+            if (innerKeySelector == null)
+                throw Error.ArgumentNull(nameof(innerKeySelector));
+            if (resultSelector == null)
+                throw Error.ArgumentNull(nameof(resultSelector));
+
+            return new GroupJoinAsyncEnumerableWithTaskAndCancellation<TOuter, TInner, TKey, TResult>(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer);
+        }
+#endif
+
         private sealed class GroupJoinAsyncEnumerable<TOuter, TInner, TKey, TResult> : IAsyncEnumerable<TResult>
         {
             private readonly IEqualityComparer<TKey> _comparer;
@@ -261,5 +297,101 @@ namespace System.Linq
                 public ValueTask DisposeAsync() => _outer.DisposeAsync();
             }
         }
+
+#if !NO_DEEP_CANCELLATION
+        private sealed class GroupJoinAsyncEnumerableWithTaskAndCancellation<TOuter, TInner, TKey, TResult> : IAsyncEnumerable<TResult>
+        {
+            private readonly IEqualityComparer<TKey> _comparer;
+            private readonly IAsyncEnumerable<TInner> _inner;
+            private readonly Func<TInner, CancellationToken, ValueTask<TKey>> _innerKeySelector;
+            private readonly IAsyncEnumerable<TOuter> _outer;
+            private readonly Func<TOuter, CancellationToken, ValueTask<TKey>> _outerKeySelector;
+            private readonly Func<TOuter, IAsyncEnumerable<TInner>, CancellationToken, ValueTask<TResult>> _resultSelector;
+
+            public GroupJoinAsyncEnumerableWithTaskAndCancellation(
+                IAsyncEnumerable<TOuter> outer,
+                IAsyncEnumerable<TInner> inner,
+                Func<TOuter, CancellationToken, ValueTask<TKey>> outerKeySelector,
+                Func<TInner, CancellationToken, ValueTask<TKey>> innerKeySelector,
+                Func<TOuter, IAsyncEnumerable<TInner>, CancellationToken, ValueTask<TResult>> resultSelector,
+                IEqualityComparer<TKey> comparer)
+            {
+                _outer = outer;
+                _inner = inner;
+                _outerKeySelector = outerKeySelector;
+                _innerKeySelector = innerKeySelector;
+                _resultSelector = resultSelector;
+                _comparer = comparer;
+            }
+
+            public IAsyncEnumerator<TResult> GetAsyncEnumerator(CancellationToken cancellationToken)
+                => new GroupJoinAsyncEnumeratorWithTask(
+                    _outer.GetAsyncEnumerator(cancellationToken),
+                    _inner,
+                    _outerKeySelector,
+                    _innerKeySelector,
+                    _resultSelector,
+                    _comparer,
+                    cancellationToken);
+
+            private sealed class GroupJoinAsyncEnumeratorWithTask : IAsyncEnumerator<TResult>
+            {
+                private readonly IEqualityComparer<TKey> _comparer;
+                private readonly IAsyncEnumerable<TInner> _inner;
+                private readonly Func<TInner, CancellationToken, ValueTask<TKey>> _innerKeySelector;
+                private readonly IAsyncEnumerator<TOuter> _outer;
+                private readonly Func<TOuter, CancellationToken, ValueTask<TKey>> _outerKeySelector;
+                private readonly Func<TOuter, IAsyncEnumerable<TInner>, CancellationToken, ValueTask<TResult>> _resultSelector;
+                private readonly CancellationToken _cancellationToken;
+
+                private Internal.LookupWithTask<TKey, TInner> _lookup;
+
+                public GroupJoinAsyncEnumeratorWithTask(
+                    IAsyncEnumerator<TOuter> outer,
+                    IAsyncEnumerable<TInner> inner,
+                    Func<TOuter, CancellationToken, ValueTask<TKey>> outerKeySelector,
+                    Func<TInner, CancellationToken, ValueTask<TKey>> innerKeySelector,
+                    Func<TOuter, IAsyncEnumerable<TInner>, CancellationToken, ValueTask<TResult>> resultSelector,
+                    IEqualityComparer<TKey> comparer,
+                    CancellationToken cancellationToken)
+                {
+                    _outer = outer;
+                    _inner = inner;
+                    _outerKeySelector = outerKeySelector;
+                    _innerKeySelector = innerKeySelector;
+                    _resultSelector = resultSelector;
+                    _comparer = comparer;
+                    _cancellationToken = cancellationToken;
+                }
+
+                public async ValueTask<bool> MoveNextAsync()
+                {
+                    // nothing to do 
+                    if (!await _outer.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        return false;
+                    }
+
+                    if (_lookup == null)
+                    {
+                        _lookup = await Internal.LookupWithTask<TKey, TInner>.CreateForJoinAsync(_inner, _innerKeySelector, _comparer, _cancellationToken).ConfigureAwait(false);
+                    }
+
+                    var item = _outer.Current;
+
+                    var outerKey = await _outerKeySelector(item, _cancellationToken).ConfigureAwait(false);
+                    var inner = _lookup[outerKey].ToAsyncEnumerable();
+
+                    Current = await _resultSelector(item, inner, _cancellationToken).ConfigureAwait(false);
+
+                    return true;
+                }
+
+                public TResult Current { get; private set; }
+
+                public ValueTask DisposeAsync() => _outer.DisposeAsync();
+            }
+        }
+#endif
     }
 }

+ 165 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Join.cs

@@ -59,6 +59,24 @@ namespace System.Linq
             return new JoinAsyncIteratorWithTask<TOuter, TInner, TKey, TResult>(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer: null);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IAsyncEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(this IAsyncEnumerable<TOuter> outer, IAsyncEnumerable<TInner> inner, Func<TOuter, CancellationToken, ValueTask<TKey>> outerKeySelector, Func<TInner, CancellationToken, ValueTask<TKey>> innerKeySelector, Func<TOuter, TInner, CancellationToken, ValueTask<TResult>> resultSelector)
+        {
+            if (outer == null)
+                throw Error.ArgumentNull(nameof(outer));
+            if (inner == null)
+                throw Error.ArgumentNull(nameof(inner));
+            if (outerKeySelector == null)
+                throw Error.ArgumentNull(nameof(outerKeySelector));
+            if (innerKeySelector == null)
+                throw Error.ArgumentNull(nameof(innerKeySelector));
+            if (resultSelector == null)
+                throw Error.ArgumentNull(nameof(resultSelector));
+
+            return new JoinAsyncIteratorWithTaskAndCancellation<TOuter, TInner, TKey, TResult>(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer: null);
+        }
+#endif
+
         public static IAsyncEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(this IAsyncEnumerable<TOuter> outer, IAsyncEnumerable<TInner> inner, Func<TOuter, ValueTask<TKey>> outerKeySelector, Func<TInner, ValueTask<TKey>> innerKeySelector, Func<TOuter, TInner, ValueTask<TResult>> resultSelector, IEqualityComparer<TKey> comparer)
         {
             if (outer == null)
@@ -75,6 +93,24 @@ namespace System.Linq
             return new JoinAsyncIteratorWithTask<TOuter, TInner, TKey, TResult>(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IAsyncEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(this IAsyncEnumerable<TOuter> outer, IAsyncEnumerable<TInner> inner, Func<TOuter, CancellationToken, ValueTask<TKey>> outerKeySelector, Func<TInner, CancellationToken, ValueTask<TKey>> innerKeySelector, Func<TOuter, TInner, CancellationToken, ValueTask<TResult>> resultSelector, IEqualityComparer<TKey> comparer)
+        {
+            if (outer == null)
+                throw Error.ArgumentNull(nameof(outer));
+            if (inner == null)
+                throw Error.ArgumentNull(nameof(inner));
+            if (outerKeySelector == null)
+                throw Error.ArgumentNull(nameof(outerKeySelector));
+            if (innerKeySelector == null)
+                throw Error.ArgumentNull(nameof(innerKeySelector));
+            if (resultSelector == null)
+                throw Error.ArgumentNull(nameof(resultSelector));
+
+            return new JoinAsyncIteratorWithTaskAndCancellation<TOuter, TInner, TKey, TResult>(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer);
+        }
+#endif
+
         internal sealed class JoinAsyncIterator<TOuter, TInner, TKey, TResult> : AsyncIterator<TResult>
         {
             private readonly IAsyncEnumerable<TOuter> _outer;
@@ -328,5 +364,134 @@ namespace System.Linq
                 return false;
             }
         }
+
+#if !NO_DEEP_CANCELLATION
+        internal sealed class JoinAsyncIteratorWithTaskAndCancellation<TOuter, TInner, TKey, TResult> : AsyncIterator<TResult>
+        {
+            private readonly IAsyncEnumerable<TOuter> _outer;
+            private readonly IAsyncEnumerable<TInner> _inner;
+            private readonly Func<TOuter, CancellationToken, ValueTask<TKey>> _outerKeySelector;
+            private readonly Func<TInner, CancellationToken, ValueTask<TKey>> _innerKeySelector;
+            private readonly Func<TOuter, TInner, CancellationToken, ValueTask<TResult>> _resultSelector;
+            private readonly IEqualityComparer<TKey> _comparer;
+
+            private IAsyncEnumerator<TOuter> _outerEnumerator;
+
+            public JoinAsyncIteratorWithTaskAndCancellation(IAsyncEnumerable<TOuter> outer, IAsyncEnumerable<TInner> inner, Func<TOuter, CancellationToken, ValueTask<TKey>> outerKeySelector, Func<TInner, CancellationToken, ValueTask<TKey>> innerKeySelector, Func<TOuter, TInner, CancellationToken, ValueTask<TResult>> resultSelector, IEqualityComparer<TKey> comparer)
+            {
+                Debug.Assert(outer != null);
+                Debug.Assert(inner != null);
+                Debug.Assert(outerKeySelector != null);
+                Debug.Assert(innerKeySelector != null);
+                Debug.Assert(resultSelector != null);
+
+                _outer = outer;
+                _inner = inner;
+                _outerKeySelector = outerKeySelector;
+                _innerKeySelector = innerKeySelector;
+                _resultSelector = resultSelector;
+                _comparer = comparer;
+            }
+
+            public override AsyncIteratorBase<TResult> Clone()
+            {
+                return new JoinAsyncIteratorWithTaskAndCancellation<TOuter, TInner, TKey, TResult>(_outer, _inner, _outerKeySelector, _innerKeySelector, _resultSelector, _comparer);
+            }
+
+            public override async ValueTask DisposeAsync()
+            {
+                if (_outerEnumerator != null)
+                {
+                    await _outerEnumerator.DisposeAsync().ConfigureAwait(false);
+                    _outerEnumerator = null;
+                }
+
+                await base.DisposeAsync().ConfigureAwait(false);
+            }
+
+            // State machine vars
+            private Internal.LookupWithTask<TKey, TInner> _lookup;
+            private int _count;
+            private TInner[] _elements;
+            private int _index;
+            private TOuter _item;
+            private int _mode;
+
+            private const int State_If = 1;
+            private const int State_DoLoop = 2;
+            private const int State_For = 3;
+            private const int State_While = 4;
+
+            protected override async ValueTask<bool> MoveNextCore()
+            {
+                switch (_state)
+                {
+                    case AsyncIteratorState.Allocated:
+                        _outerEnumerator = _outer.GetAsyncEnumerator(_cancellationToken);
+                        _mode = State_If;
+                        _state = AsyncIteratorState.Iterating;
+                        goto case AsyncIteratorState.Iterating;
+
+                    case AsyncIteratorState.Iterating:
+                        switch (_mode)
+                        {
+                            case State_If:
+                                if (await _outerEnumerator.MoveNextAsync().ConfigureAwait(false))
+                                {
+                                    _lookup = await Internal.LookupWithTask<TKey, TInner>.CreateForJoinAsync(_inner, _innerKeySelector, _comparer, _cancellationToken).ConfigureAwait(false);
+
+                                    if (_lookup.Count != 0)
+                                    {
+                                        _mode = State_DoLoop;
+                                        goto case State_DoLoop;
+                                    }
+                                }
+
+                                break;
+
+                            case State_DoLoop:
+                                _item = _outerEnumerator.Current;
+                                var g = _lookup.GetGrouping(await _outerKeySelector(_item, _cancellationToken).ConfigureAwait(false), create: false);
+                                if (g != null)
+                                {
+                                    _count = g._count;
+                                    _elements = g._elements;
+                                    _index = 0;
+                                    _mode = State_For;
+                                    goto case State_For;
+                                }
+
+                                // advance to while
+                                _mode = State_While;
+                                goto case State_While;
+
+                            case State_For:
+                                _current = await _resultSelector(_item, _elements[_index], _cancellationToken).ConfigureAwait(false);
+                                _index++;
+                                if (_index == _count)
+                                {
+                                    _mode = State_While;
+                                }
+
+                                return true;
+
+                            case State_While:
+                                var hasNext = await _outerEnumerator.MoveNextAsync().ConfigureAwait(false);
+                                if (hasNext)
+                                {
+                                    goto case State_DoLoop;
+                                }
+
+                                break;
+                        }
+
+                        await DisposeAsync().ConfigureAwait(false);
+                        break;
+                }
+
+                return false;
+            }
+        }
+#endif
     }
 }

+ 21 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Last.cs

@@ -66,6 +66,18 @@ namespace System.Linq
             return LastCore(source, predicate, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<TSource> LastAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (predicate == null)
+                throw Error.ArgumentNull(nameof(predicate));
+
+            return LastCore(source, predicate, cancellationToken);
+        }
+#endif
+
         private static async Task<TSource> LastCore<TSource>(IAsyncEnumerable<TSource> source, CancellationToken cancellationToken)
         {
             var last = await TryGetLast(source, cancellationToken).ConfigureAwait(false);
@@ -86,5 +98,14 @@ namespace System.Linq
 
             return last.HasValue ? last.Value : throw Error.NoElements();
         }
+
+#if !NO_DEEP_CANCELLATION
+        private static async Task<TSource> LastCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate, CancellationToken cancellationToken)
+        {
+            var last = await TryGetLast(source, predicate, cancellationToken).ConfigureAwait(false);
+
+            return last.HasValue ? last.Value : throw Error.NoElements();
+        }
+#endif
     }
 }

+ 56 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/LastOrDefault.cs

@@ -66,6 +66,18 @@ namespace System.Linq
             return LastOrDefaultCore(source, predicate, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<TSource> LastOrDefaultAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (predicate == null)
+                throw Error.ArgumentNull(nameof(predicate));
+
+            return LastOrDefaultCore(source, predicate, cancellationToken);
+        }
+#endif
+
         private static async Task<TSource> LastOrDefaultCore<TSource>(IAsyncEnumerable<TSource> source, CancellationToken cancellationToken)
         {
             var last = await TryGetLast(source, cancellationToken).ConfigureAwait(false);
@@ -87,6 +99,15 @@ namespace System.Linq
             return last.HasValue ? last.Value : default;
         }
 
+#if !NO_DEEP_CANCELLATION
+        private static async Task<TSource> LastOrDefaultCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate, CancellationToken cancellationToken)
+        {
+            var last = await TryGetLast(source, predicate, cancellationToken).ConfigureAwait(false);
+
+            return last.HasValue ? last.Value : default;
+        }
+#endif
+
         private static ValueTask<Maybe<TSource>> TryGetLast<TSource>(IAsyncEnumerable<TSource> source, CancellationToken cancellationToken)
         {
             if (source is IList<TSource> list)
@@ -197,5 +218,40 @@ namespace System.Linq
 
             return new Maybe<TSource>();
         }
+
+#if !NO_DEEP_CANCELLATION
+        private static async Task<Maybe<TSource>> TryGetLast<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate, CancellationToken cancellationToken)
+        {
+            var last = default(TSource);
+            var hasLast = false;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var value = e.Current;
+
+                    if (await predicate(value, cancellationToken).ConfigureAwait(false))
+                    {
+                        hasLast = true;
+                        last = value;
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            if (hasLast)
+            {
+                return new Maybe<TSource>(last);
+            }
+
+            return new Maybe<TSource>();
+        }
+#endif
     }
 }

+ 41 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/LongCount.cs

@@ -66,6 +66,18 @@ namespace System.Linq
             return LongCountCore(source, predicate, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<long> LongCountAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (predicate == null)
+                throw Error.ArgumentNull(nameof(predicate));
+
+            return LongCountCore(source, predicate, cancellationToken);
+        }
+#endif
+
         private static async Task<long> LongCountCore<TSource>(IAsyncEnumerable<TSource> source, CancellationToken cancellationToken)
         {
             var count = 0L;
@@ -143,5 +155,34 @@ namespace System.Linq
 
             return count;
         }
+
+#if !NO_DEEP_CANCELLATION
+        private static async Task<long> LongCountCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate, CancellationToken cancellationToken)
+        {
+            var count = 0L;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    if (await predicate(e.Current, cancellationToken).ConfigureAwait(false))
+                    {
+                        checked
+                        {
+                            count++;
+                        }
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return count;
+        }
+#endif
     }
 }

+ 127 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Lookup.cs

@@ -398,6 +398,37 @@ namespace System.Linq.Internal
             return lookup;
         }
 
+#if !NO_DEEP_CANCELLATION
+        internal static async Task<LookupWithTask<TKey, TElement>> CreateAsync<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TSource, CancellationToken, ValueTask<TElement>> elementSelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
+        {
+            Debug.Assert(source != null);
+            Debug.Assert(keySelector != null);
+            Debug.Assert(elementSelector != null);
+
+            var lookup = new LookupWithTask<TKey, TElement>(comparer);
+
+            var enu = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await enu.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var key = await keySelector(enu.Current, cancellationToken).ConfigureAwait(false);
+                    var group = lookup.GetGrouping(key, create: true);
+
+                    var element = await elementSelector(enu.Current, cancellationToken).ConfigureAwait(false);
+                    group.Add(element);
+                }
+            }
+            finally
+            {
+                await enu.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return lookup;
+        }
+#endif
+
         internal static async Task<LookupWithTask<TKey, TElement>> CreateAsync(IAsyncEnumerable<TElement> source, Func<TElement, ValueTask<TKey>> keySelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
         {
             Debug.Assert(source != null);
@@ -423,6 +454,33 @@ namespace System.Linq.Internal
             return lookup;
         }
 
+#if !NO_DEEP_CANCELLATION
+        internal static async Task<LookupWithTask<TKey, TElement>> CreateAsync(IAsyncEnumerable<TElement> source, Func<TElement, CancellationToken, ValueTask<TKey>> keySelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
+        {
+            Debug.Assert(source != null);
+            Debug.Assert(keySelector != null);
+
+            var lookup = new LookupWithTask<TKey, TElement>(comparer);
+
+            var enu = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await enu.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var key = await keySelector(enu.Current, cancellationToken).ConfigureAwait(false);
+                    lookup.GetGrouping(key, create: true).Add(enu.Current);
+                }
+            }
+            finally
+            {
+                await enu.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return lookup;
+        }
+#endif
+
         internal static async Task<LookupWithTask<TKey, TElement>> CreateForJoinAsync(IAsyncEnumerable<TElement> source, Func<TElement, ValueTask<TKey>> keySelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
         {
             var lookup = new LookupWithTask<TKey, TElement>(comparer);
@@ -448,6 +506,33 @@ namespace System.Linq.Internal
             return lookup;
         }
 
+#if !NO_DEEP_CANCELLATION
+        internal static async Task<LookupWithTask<TKey, TElement>> CreateForJoinAsync(IAsyncEnumerable<TElement> source, Func<TElement, CancellationToken, ValueTask<TKey>> keySelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
+        {
+            var lookup = new LookupWithTask<TKey, TElement>(comparer);
+
+            var enu = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await enu.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var key = await keySelector(enu.Current, cancellationToken).ConfigureAwait(false);
+                    if (key != null)
+                    {
+                        lookup.GetGrouping(key, create: true).Add(enu.Current);
+                    }
+                }
+            }
+            finally
+            {
+                await enu.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return lookup;
+        }
+#endif
+
         internal Grouping<TKey, TElement> GetGrouping(TKey key, bool create)
         {
             var hashCode = InternalGetHashCode(key);
@@ -518,6 +603,27 @@ namespace System.Linq.Internal
             return array;
         }
 
+#if !NO_DEEP_CANCELLATION
+        internal async Task<TResult[]> ToArray<TResult>(Func<TKey, IAsyncEnumerable<TElement>, CancellationToken, ValueTask<TResult>> resultSelector, CancellationToken cancellationToken)
+        {
+            var array = new TResult[Count];
+            var index = 0;
+            var g = _lastGrouping;
+            if (g != null)
+            {
+                do
+                {
+                    g = g._next;
+                    g.Trim();
+                    array[index] = await resultSelector(g._key, g._elements.ToAsyncEnumerable(), cancellationToken).ConfigureAwait(false);
+                    ++index;
+                } while (g != _lastGrouping);
+            }
+
+            return array;
+        }
+#endif
+
         internal async Task<List<TResult>> ToList<TResult>(Func<TKey, IAsyncEnumerable<TElement>, ValueTask<TResult>> resultSelector)
         {
             var list = new List<TResult>(Count);
@@ -537,6 +643,27 @@ namespace System.Linq.Internal
             return list;
         }
 
+#if !NO_DEEP_CANCELLATION
+        internal async Task<List<TResult>> ToList<TResult>(Func<TKey, IAsyncEnumerable<TElement>, CancellationToken, ValueTask<TResult>> resultSelector, CancellationToken cancellationToken)
+        {
+            var list = new List<TResult>(Count);
+            var g = _lastGrouping;
+            if (g != null)
+            {
+                do
+                {
+                    g = g._next;
+                    g.Trim();
+
+                    var result = await resultSelector(g._key, g._elements.ToAsyncEnumerable(), cancellationToken).ConfigureAwait(false);
+                    list.Add(result);
+                } while (g != _lastGrouping);
+            }
+
+            return list;
+        }
+#endif
+
         private void Resize()
         {
             var newSize = checked((Count * 2) + 1);

+ 67 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Max.Generic.cs

@@ -204,5 +204,72 @@ namespace System.Linq
 
             return value;
         }
+
+#if !NO_DEEP_CANCELLATION
+        private static async Task<TResult> MaxCore<TSource, TResult>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TResult>> selector, CancellationToken cancellationToken)
+        {
+            var comparer = Comparer<TResult>.Default;
+            var value = default(TResult);
+            if (value == null)
+            {
+                var e = source.GetAsyncEnumerator(cancellationToken);
+
+                try
+                {
+                    do
+                    {
+                        if (!await e.MoveNextAsync().ConfigureAwait(false))
+                        {
+                            return value;
+                        }
+
+                        value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    }
+                    while (value == null);
+
+                    while (await e.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        var x = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                        if (x != null && comparer.Compare(x, value) > 0)
+                        {
+                            value = x;
+                        }
+                    }
+                }
+                finally
+                {
+                    await e.DisposeAsync().ConfigureAwait(false);
+                }
+            }
+            else
+            {
+                var e = source.GetAsyncEnumerator(cancellationToken);
+
+                try
+                {
+                    if (!await e.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        throw Error.NoElements();
+                    }
+
+                    value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    while (await e.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        var x = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                        if (comparer.Compare(x, value) > 0)
+                        {
+                            value = x;
+                        }
+                    }
+                }
+                finally
+                {
+                    await e.DisposeAsync().ConfigureAwait(false);
+                }
+            }
+
+            return value;
+        }
+#endif
     }
 }

+ 455 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Max.Primitive.cs

@@ -1368,5 +1368,460 @@ namespace System.Linq
 
             return value;
         }
+
+#if !NO_DEEP_CANCELLATION
+        private static async Task<int> MaxCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<int>> selector, CancellationToken cancellationToken)
+        {
+            int value;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                if (!await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    throw Error.NoElements();
+                }
+
+                value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var x = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    if (x > value)
+                    {
+                        value = x;
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return value;
+        }
+
+        private static async Task<int?> MaxCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<int?>> selector, CancellationToken cancellationToken)
+        {
+            int? value = null;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                do
+                {
+                    if (!await e.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        return value;
+                    }
+
+                    value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                }
+                while (!value.HasValue);
+
+                var valueVal = value.GetValueOrDefault();
+                if (valueVal >= 0)
+                {
+                    // We can fast-path this case where we know HasValue will
+                    // never affect the outcome, without constantly checking
+                    // if we're in such a state. Similar fast-paths could
+                    // be done for other cases, but as all-positive
+                    // or mostly-positive integer values are quite common in real-world
+                    // uses, it's only been done in this direction for int? and long?.
+                    while (await e.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        var cur = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                        var x = cur.GetValueOrDefault();
+                        if (x > valueVal)
+                        {
+                            valueVal = x;
+                            value = cur;
+                        }
+                    }
+                }
+                else
+                {
+                    while (await e.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        var cur = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                        var x = cur.GetValueOrDefault();
+
+                        // Do not replace & with &&. The branch prediction cost outweighs the extra operation
+                        // unless nulls either never happen or always happen.
+                        if (cur.HasValue & x > valueVal)
+                        {
+                            valueVal = x;
+                            value = cur;
+                        }
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return value;
+        }
+
+        private static async Task<long> MaxCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<long>> selector, CancellationToken cancellationToken)
+        {
+            long value;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                if (!await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    throw Error.NoElements();
+                }
+
+                value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var x = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    if (x > value)
+                    {
+                        value = x;
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return value;
+        }
+
+        private static async Task<long?> MaxCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<long?>> selector, CancellationToken cancellationToken)
+        {
+            long? value = null;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                do
+                {
+                    if (!await e.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        return value;
+                    }
+
+                    value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                }
+                while (!value.HasValue);
+
+                var valueVal = value.GetValueOrDefault();
+                if (valueVal >= 0)
+                {
+                    while (await e.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        var cur = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                        var x = cur.GetValueOrDefault();
+                        if (x > valueVal)
+                        {
+                            valueVal = x;
+                            value = cur;
+                        }
+                    }
+                }
+                else
+                {
+                    while (await e.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        var cur = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                        var x = cur.GetValueOrDefault();
+
+                        // Do not replace & with &&. The branch prediction cost outweighs the extra operation
+                        // unless nulls either never happen or always happen.
+                        if (cur.HasValue & x > valueVal)
+                        {
+                            valueVal = x;
+                            value = cur;
+                        }
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return value;
+        }
+
+        private static async Task<float> MaxCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<float>> selector, CancellationToken cancellationToken)
+        {
+            float value;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                if (!await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    throw Error.NoElements();
+                }
+
+                value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                while (float.IsNaN(value))
+                {
+                    if (!await e.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        return value;
+                    }
+
+                    value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                }
+
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var x = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    if (x > value)
+                    {
+                        value = x;
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return value;
+        }
+
+        private static async Task<float?> MaxCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<float?>> selector, CancellationToken cancellationToken)
+        {
+            float? value = null;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                do
+                {
+                    if (!await e.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        return value;
+                    }
+
+                    value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                }
+                while (!value.HasValue);
+
+                var valueVal = value.GetValueOrDefault();
+                while (float.IsNaN(valueVal))
+                {
+                    if (!await e.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        return value;
+                    }
+
+                    var cur = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    if (cur.HasValue)
+                    {
+                        valueVal = (value = cur).GetValueOrDefault();
+                    }
+                }
+
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var cur = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    var x = cur.GetValueOrDefault();
+
+                    // Do not replace & with &&. The branch prediction cost outweighs the extra operation
+                    // unless nulls either never happen or always happen.
+                    if (cur.HasValue & x > valueVal)
+                    {
+                        valueVal = x;
+                        value = cur;
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return value;
+        }
+
+        private static async Task<double> MaxCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<double>> selector, CancellationToken cancellationToken)
+        {
+            double value;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                if (!await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    throw Error.NoElements();
+                }
+
+                value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+
+                // As described in a comment on Min(IAsyncEnumerable<double>) NaN is ordered
+                // less than all other values. We need to do explicit checks to ensure this, but
+                // once we've found a value that is not NaN we need no longer worry about it,
+                // so first loop until such a value is found (or not, as the case may be).
+                while (double.IsNaN(value))
+                {
+                    if (!await e.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        return value;
+                    }
+
+                    value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                }
+
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var x = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    if (x > value)
+                    {
+                        value = x;
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return value;
+        }
+
+        private static async Task<double?> MaxCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<double?>> selector, CancellationToken cancellationToken)
+        {
+            double? value = null;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                do
+                {
+                    if (!await e.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        return value;
+                    }
+
+                    value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                }
+                while (!value.HasValue);
+
+                var valueVal = value.GetValueOrDefault();
+                while (double.IsNaN(valueVal))
+                {
+                    if (!await e.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        return value;
+                    }
+
+                    var cur = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    if (cur.HasValue)
+                    {
+                        valueVal = (value = cur).GetValueOrDefault();
+                    }
+                }
+
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var cur = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    var x = cur.GetValueOrDefault();
+
+                    // Do not replace & with &&. The branch prediction cost outweighs the extra operation
+                    // unless nulls either never happen or always happen.
+                    if (cur.HasValue & x > valueVal)
+                    {
+                        valueVal = x;
+                        value = cur;
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return value;
+        }
+
+        private static async Task<decimal> MaxCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<decimal>> selector, CancellationToken cancellationToken)
+        {
+            decimal value;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                if (!await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    throw Error.NoElements();
+                }
+
+                value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var x = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    if (x > value)
+                    {
+                        value = x;
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return value;
+        }
+
+        private static async Task<decimal?> MaxCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<decimal?>> selector, CancellationToken cancellationToken)
+        {
+            decimal? value = null;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                do
+                {
+                    if (!await e.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        return value;
+                    }
+
+                    value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                }
+                while (!value.HasValue);
+
+                var valueVal = value.GetValueOrDefault();
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var cur = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    var x = cur.GetValueOrDefault();
+                    if (cur.HasValue && x > valueVal)
+                    {
+                        valueVal = x;
+                        value = cur;
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return value;
+        }
+#endif
     }
 }

+ 12 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Max.cs

@@ -65,5 +65,17 @@ namespace System.Linq
 
             return MaxCore(source, selector, cancellationToken);
         }
+
+#if !NO_DEEP_CANCELLATION
+        public static Task<TResult> MaxAsync<TSource, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TResult>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return MaxCore(source, selector, cancellationToken);
+        }
+#endif
     }
 }

+ 67 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Min.Generic.cs

@@ -204,5 +204,72 @@ namespace System.Linq
 
             return value;
         }
+
+#if !NO_DEEP_CANCELLATION
+        private static async Task<TResult> MinCore<TSource, TResult>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TResult>> selector, CancellationToken cancellationToken)
+        {
+            var comparer = Comparer<TResult>.Default;
+            var value = default(TResult);
+            if (value == null)
+            {
+                var e = source.GetAsyncEnumerator(cancellationToken);
+
+                try
+                {
+                    do
+                    {
+                        if (!await e.MoveNextAsync().ConfigureAwait(false))
+                        {
+                            return value;
+                        }
+
+                        value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    }
+                    while (value == null);
+
+                    while (await e.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        var x = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                        if (x != null && comparer.Compare(x, value) < 0)
+                        {
+                            value = x;
+                        }
+                    }
+                }
+                finally
+                {
+                    await e.DisposeAsync().ConfigureAwait(false);
+                }
+            }
+            else
+            {
+                var e = source.GetAsyncEnumerator(cancellationToken);
+
+                try
+                {
+                    if (!await e.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        throw Error.NoElements();
+                    }
+
+                    value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    while (await e.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        var x = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                        if (comparer.Compare(x, value) < 0)
+                        {
+                            value = x;
+                        }
+                    }
+                }
+                finally
+                {
+                    await e.DisposeAsync().ConfigureAwait(false);
+                }
+            }
+
+            return value;
+        }
+#endif
     }
 }

+ 393 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Min.Primitive.cs

@@ -1182,5 +1182,398 @@ namespace System.Linq
 
             return value;
         }
+
+#if !NO_DEEP_CANCELLATION
+        private static async Task<int> MinCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<int>> selector, CancellationToken cancellationToken)
+        {
+            int value;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                if (!await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    throw Error.NoElements();
+                }
+
+                value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var x = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    if (x < value)
+                    {
+                        value = x;
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return value;
+        }
+
+        private static async Task<int?> MinCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<int?>> selector, CancellationToken cancellationToken)
+        {
+            int? value = null;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                // Start off knowing that we've a non-null value (or exit here, knowing we don't)
+                // so we don't have to keep testing for nullity.
+                do
+                {
+                    if (!await e.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        return value;
+                    }
+
+                    value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                }
+                while (!value.HasValue);
+
+                // Keep hold of the wrapped value, and do comparisons on that, rather than
+                // using the lifted operation each time.
+                var valueVal = value.GetValueOrDefault();
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var cur = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    var x = cur.GetValueOrDefault();
+
+                    // Do not replace & with &&. The branch prediction cost outweighs the extra operation
+                    // unless nulls either never happen or always happen.
+                    if (cur.HasValue & x < valueVal)
+                    {
+                        valueVal = x;
+                        value = cur;
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return value;
+        }
+
+        private static async Task<long> MinCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<long>> selector, CancellationToken cancellationToken)
+        {
+            long value;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                if (!await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    throw Error.NoElements();
+                }
+
+                value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var x = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    if (x < value)
+                    {
+                        value = x;
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return value;
+        }
+
+        private static async Task<long?> MinCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<long?>> selector, CancellationToken cancellationToken)
+        {
+            long? value = null;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                do
+                {
+                    if (!await e.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        return value;
+                    }
+
+                    value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                }
+                while (!value.HasValue);
+
+                var valueVal = value.GetValueOrDefault();
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var cur = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    var x = cur.GetValueOrDefault();
+
+                    // Do not replace & with &&. The branch prediction cost outweighs the extra operation
+                    // unless nulls either never happen or always happen.
+                    if (cur.HasValue & x < valueVal)
+                    {
+                        valueVal = x;
+                        value = cur;
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return value;
+        }
+
+        private static async Task<float> MinCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<float>> selector, CancellationToken cancellationToken)
+        {
+            float value;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                if (!await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    throw Error.NoElements();
+                }
+
+                value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var x = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    if (x < value)
+                    {
+                        value = x;
+                    }
+
+                    // Normally NaN < anything is false, as is anything < NaN
+                    // However, this leads to some irksome outcomes in Min and Max.
+                    // If we use those semantics then Min(NaN, 5.0) is NaN, but
+                    // Min(5.0, NaN) is 5.0!  To fix this, we impose a total
+                    // ordering where NaN is smaller than every value, including
+                    // negative infinity.
+                    // Not testing for NaN therefore isn't an option, but since we
+                    // can't find a smaller value, we can short-circuit.
+                    else if (float.IsNaN(x))
+                    {
+                        return x;
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return value;
+        }
+
+        private static async Task<float?> MinCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<float?>> selector, CancellationToken cancellationToken)
+        {
+            float? value = null;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                do
+                {
+                    if (!await e.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        return value;
+                    }
+
+                    value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                }
+                while (!value.HasValue);
+
+                var valueVal = value.GetValueOrDefault();
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var cur = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    if (cur.HasValue)
+                    {
+                        var x = cur.GetValueOrDefault();
+                        if (x < valueVal)
+                        {
+                            valueVal = x;
+                            value = cur;
+                        }
+                        else if (float.IsNaN(x))
+                        {
+                            return cur;
+                        }
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return value;
+        }
+
+        private static async Task<double> MinCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<double>> selector, CancellationToken cancellationToken)
+        {
+            double value;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                if (!await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    throw Error.NoElements();
+                }
+
+                value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var x = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    if (x < value)
+                    {
+                        value = x;
+                    }
+                    else if (double.IsNaN(x))
+                    {
+                        return x;
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return value;
+        }
+
+        private static async Task<double?> MinCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<double?>> selector, CancellationToken cancellationToken)
+        {
+            double? value = null;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                do
+                {
+                    if (!await e.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        return value;
+                    }
+
+                    value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                }
+                while (!value.HasValue);
+
+                var valueVal = value.GetValueOrDefault();
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var cur = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    if (cur.HasValue)
+                    {
+                        var x = cur.GetValueOrDefault();
+                        if (x < valueVal)
+                        {
+                            valueVal = x;
+                            value = cur;
+                        }
+                        else if (double.IsNaN(x))
+                        {
+                            return cur;
+                        }
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return value;
+        }
+
+        private static async Task<decimal> MinCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<decimal>> selector, CancellationToken cancellationToken)
+        {
+            decimal value;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                if (!await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    throw Error.NoElements();
+                }
+
+                value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var x = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    if (x < value)
+                    {
+                        value = x;
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return value;
+        }
+
+        private static async Task<decimal?> MinCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<decimal?>> selector, CancellationToken cancellationToken)
+        {
+            decimal? value = null;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                do
+                {
+                    if (!await e.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        return value;
+                    }
+
+                    value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                }
+                while (!value.HasValue);
+
+                var valueVal = value.GetValueOrDefault();
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var cur = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+                    var x = cur.GetValueOrDefault();
+                    if (cur.HasValue && x < valueVal)
+                    {
+                        valueVal = x;
+                        value = cur;
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return value;
+        }
+#endif
     }
 }

+ 12 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Min.cs

@@ -65,5 +65,17 @@ namespace System.Linq
 
             return MinCore(source, selector, cancellationToken);
         }
+
+#if !NO_DEEP_CANCELLATION
+        public static Task<TResult> MinAsync<TSource, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TResult>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return MinCore(source, selector, cancellationToken);
+        }
+#endif
     }
 }

+ 240 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/MinMax.Generated.cs

@@ -66,6 +66,18 @@ namespace System.Linq
             return MaxCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<int> MaxAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<int>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return MaxCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<int?> MaxAsync(this IAsyncEnumerable<int?> source)
         {
             if (source == null)
@@ -122,6 +134,18 @@ namespace System.Linq
             return MaxCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<int?> MaxAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<int?>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return MaxCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<long> MaxAsync(this IAsyncEnumerable<long> source)
         {
             if (source == null)
@@ -178,6 +202,18 @@ namespace System.Linq
             return MaxCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<long> MaxAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<long>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return MaxCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<long?> MaxAsync(this IAsyncEnumerable<long?> source)
         {
             if (source == null)
@@ -234,6 +270,18 @@ namespace System.Linq
             return MaxCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<long?> MaxAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<long?>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return MaxCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<float> MaxAsync(this IAsyncEnumerable<float> source)
         {
             if (source == null)
@@ -290,6 +338,18 @@ namespace System.Linq
             return MaxCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<float> MaxAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<float>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return MaxCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<float?> MaxAsync(this IAsyncEnumerable<float?> source)
         {
             if (source == null)
@@ -346,6 +406,18 @@ namespace System.Linq
             return MaxCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<float?> MaxAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<float?>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return MaxCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<double> MaxAsync(this IAsyncEnumerable<double> source)
         {
             if (source == null)
@@ -402,6 +474,18 @@ namespace System.Linq
             return MaxCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<double> MaxAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<double>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return MaxCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<double?> MaxAsync(this IAsyncEnumerable<double?> source)
         {
             if (source == null)
@@ -458,6 +542,18 @@ namespace System.Linq
             return MaxCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<double?> MaxAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<double?>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return MaxCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<decimal> MaxAsync(this IAsyncEnumerable<decimal> source)
         {
             if (source == null)
@@ -514,6 +610,18 @@ namespace System.Linq
             return MaxCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<decimal> MaxAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<decimal>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return MaxCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<decimal?> MaxAsync(this IAsyncEnumerable<decimal?> source)
         {
             if (source == null)
@@ -570,6 +678,18 @@ namespace System.Linq
             return MaxCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<decimal?> MaxAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<decimal?>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return MaxCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<int> MinAsync(this IAsyncEnumerable<int> source)
         {
             if (source == null)
@@ -626,6 +746,18 @@ namespace System.Linq
             return MinCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<int> MinAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<int>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return MinCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<int?> MinAsync(this IAsyncEnumerable<int?> source)
         {
             if (source == null)
@@ -682,6 +814,18 @@ namespace System.Linq
             return MinCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<int?> MinAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<int?>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return MinCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<long> MinAsync(this IAsyncEnumerable<long> source)
         {
             if (source == null)
@@ -738,6 +882,18 @@ namespace System.Linq
             return MinCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<long> MinAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<long>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return MinCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<long?> MinAsync(this IAsyncEnumerable<long?> source)
         {
             if (source == null)
@@ -794,6 +950,18 @@ namespace System.Linq
             return MinCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<long?> MinAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<long?>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return MinCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<float> MinAsync(this IAsyncEnumerable<float> source)
         {
             if (source == null)
@@ -850,6 +1018,18 @@ namespace System.Linq
             return MinCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<float> MinAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<float>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return MinCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<float?> MinAsync(this IAsyncEnumerable<float?> source)
         {
             if (source == null)
@@ -906,6 +1086,18 @@ namespace System.Linq
             return MinCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<float?> MinAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<float?>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return MinCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<double> MinAsync(this IAsyncEnumerable<double> source)
         {
             if (source == null)
@@ -962,6 +1154,18 @@ namespace System.Linq
             return MinCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<double> MinAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<double>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return MinCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<double?> MinAsync(this IAsyncEnumerable<double?> source)
         {
             if (source == null)
@@ -1018,6 +1222,18 @@ namespace System.Linq
             return MinCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<double?> MinAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<double?>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return MinCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<decimal> MinAsync(this IAsyncEnumerable<decimal> source)
         {
             if (source == null)
@@ -1074,6 +1290,18 @@ namespace System.Linq
             return MinCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<decimal> MinAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<decimal>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return MinCore(source, selector, cancellationToken);
+        }
+#endif
+
         public static Task<decimal?> MinAsync(this IAsyncEnumerable<decimal?> source)
         {
             if (source == null)
@@ -1130,5 +1358,17 @@ namespace System.Linq
             return MinCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<decimal?> MinAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<decimal?>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return MinCore(source, selector, cancellationToken);
+        }
+#endif
+
     }
 }

+ 12 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/MinMax.Generated.tt

@@ -78,6 +78,18 @@ foreach (var m in new[] { "Max", "Min" })
             return <#=m#>Core(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<<#=t#>> <#=m#>Async<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<<#=t#>>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return <#=m#>Core(source, selector, cancellationToken);
+        }
+#endif
+
 <#
     }
 }

+ 94 - 8
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/OrderBy.cs

@@ -3,6 +3,7 @@
 // See the LICENSE file in the project root for more information. 
 
 using System.Collections.Generic;
+using System.Threading;
 using System.Threading.Tasks;
 
 namespace System.Linq
@@ -16,7 +17,7 @@ namespace System.Linq
             if (keySelector == null)
                 throw Error.ArgumentNull(nameof(keySelector));
 
-            return OrderBy(source, keySelector, comparer: null);
+            return new OrderedAsyncEnumerable<TSource, TKey>(source, keySelector, comparer: null, descending: false, parent: null);
         }
 
         public static IOrderedAsyncEnumerable<TSource> OrderBy<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<TKey>> keySelector)
@@ -26,7 +27,7 @@ namespace System.Linq
             if (keySelector == null)
                 throw Error.ArgumentNull(nameof(keySelector));
 
-            return OrderBy<TSource, TKey>(source, keySelector, comparer: null);
+            return new OrderedAsyncEnumerableWithTask<TSource, TKey>(source, keySelector, comparer: null, descending: false, parent: null);
         }
 
         public static IOrderedAsyncEnumerable<TSource> OrderBy<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
@@ -38,6 +39,7 @@ namespace System.Linq
 
             return new OrderedAsyncEnumerable<TSource, TKey>(source, keySelector, comparer, descending: false, parent: null);
         }
+
         public static IOrderedAsyncEnumerable<TSource> OrderBy<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<TKey>> keySelector, IComparer<TKey> comparer)
         {
             if (source == null)
@@ -48,6 +50,18 @@ namespace System.Linq
             return new OrderedAsyncEnumerableWithTask<TSource, TKey>(source, keySelector, comparer, descending: false, parent: null);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IOrderedAsyncEnumerable<TSource> OrderBy<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, IComparer<TKey> comparer)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (keySelector == null)
+                throw Error.ArgumentNull(nameof(keySelector));
+
+            return new OrderedAsyncEnumerableWithTaskAndCancellation<TSource, TKey>(source, keySelector, comparer, descending: false, parent: null);
+        }
+#endif
+
         public static IOrderedAsyncEnumerable<TSource> OrderByDescending<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector)
         {
             if (source == null)
@@ -55,7 +69,7 @@ namespace System.Linq
             if (keySelector == null)
                 throw Error.ArgumentNull(nameof(keySelector));
 
-            return source.OrderByDescending(keySelector, comparer: null);
+            return new OrderedAsyncEnumerable<TSource, TKey>(source, keySelector, comparer: null, descending: true, parent: null);
         }
 
         public static IOrderedAsyncEnumerable<TSource> OrderByDescending<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<TKey>> keySelector)
@@ -65,8 +79,20 @@ namespace System.Linq
             if (keySelector == null)
                 throw Error.ArgumentNull(nameof(keySelector));
 
-            return source.OrderByDescending<TSource, TKey>(keySelector, comparer: null);
+            return new OrderedAsyncEnumerableWithTask<TSource, TKey>(source, keySelector, comparer: null, descending: true, parent: null);
+        }
+
+#if !NO_DEEP_CANCELLATION
+        public static IOrderedAsyncEnumerable<TSource> OrderByDescending<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (keySelector == null)
+                throw Error.ArgumentNull(nameof(keySelector));
+
+            return new OrderedAsyncEnumerableWithTaskAndCancellation<TSource, TKey>(source, keySelector, comparer: null, descending: true, parent: null);
         }
+#endif
 
         public static IOrderedAsyncEnumerable<TSource> OrderByDescending<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
         {
@@ -88,6 +114,18 @@ namespace System.Linq
             return new OrderedAsyncEnumerableWithTask<TSource, TKey>(source, keySelector, comparer, descending: true, parent: null);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IOrderedAsyncEnumerable<TSource> OrderByDescending<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, IComparer<TKey> comparer)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (keySelector == null)
+                throw Error.ArgumentNull(nameof(keySelector));
+
+            return new OrderedAsyncEnumerableWithTaskAndCancellation<TSource, TKey>(source, keySelector, comparer, descending: true, parent: null);
+        }
+#endif
+
         public static IOrderedAsyncEnumerable<TSource> ThenBy<TSource, TKey>(this IOrderedAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector)
         {
             if (source == null)
@@ -95,7 +133,7 @@ namespace System.Linq
             if (keySelector == null)
                 throw Error.ArgumentNull(nameof(keySelector));
 
-            return source.ThenBy(keySelector, Comparer<TKey>.Default);
+            return source.CreateOrderedEnumerable(keySelector, comparer: null, descending: false);
         }
 
         public static IOrderedAsyncEnumerable<TSource> ThenBy<TSource, TKey>(this IOrderedAsyncEnumerable<TSource> source, Func<TSource, ValueTask<TKey>> keySelector)
@@ -105,9 +143,21 @@ namespace System.Linq
             if (keySelector == null)
                 throw Error.ArgumentNull(nameof(keySelector));
 
-            return source.ThenBy(keySelector, Comparer<TKey>.Default);
+            return source.CreateOrderedEnumerable(keySelector, comparer: default(IComparer<TKey>), descending: false);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IOrderedAsyncEnumerable<TSource> ThenBy<TSource, TKey>(this IOrderedAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (keySelector == null)
+                throw Error.ArgumentNull(nameof(keySelector));
+
+            return source.CreateOrderedEnumerable(keySelector, comparer: null, descending: false);
+        }
+#endif
+
         public static IOrderedAsyncEnumerable<TSource> ThenBy<TSource, TKey>(this IOrderedAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
         {
             if (source == null)
@@ -128,6 +178,18 @@ namespace System.Linq
             return source.CreateOrderedEnumerable(keySelector, comparer, descending: false);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IOrderedAsyncEnumerable<TSource> ThenBy<TSource, TKey>(this IOrderedAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, IComparer<TKey> comparer)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (keySelector == null)
+                throw Error.ArgumentNull(nameof(keySelector));
+
+            return source.CreateOrderedEnumerable(keySelector, comparer, descending: false);
+        }
+#endif
+
         public static IOrderedAsyncEnumerable<TSource> ThenByDescending<TSource, TKey>(this IOrderedAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector)
         {
             if (source == null)
@@ -135,7 +197,7 @@ namespace System.Linq
             if (keySelector == null)
                 throw Error.ArgumentNull(nameof(keySelector));
 
-            return source.CreateOrderedEnumerable(keySelector, Comparer<TKey>.Default, descending: true);
+            return source.CreateOrderedEnumerable(keySelector, comparer: null, descending: true);
         }
 
         public static IOrderedAsyncEnumerable<TSource> ThenByDescending<TSource, TKey>(this IOrderedAsyncEnumerable<TSource> source, Func<TSource, ValueTask<TKey>> keySelector)
@@ -145,9 +207,21 @@ namespace System.Linq
             if (keySelector == null)
                 throw Error.ArgumentNull(nameof(keySelector));
 
-            return source.CreateOrderedEnumerable(keySelector, Comparer<TKey>.Default, descending: true);
+            return source.CreateOrderedEnumerable(keySelector, comparer: default(IComparer<TKey>), descending: true);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IOrderedAsyncEnumerable<TSource> ThenByDescending<TSource, TKey>(this IOrderedAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (keySelector == null)
+                throw Error.ArgumentNull(nameof(keySelector));
+
+            return source.CreateOrderedEnumerable(keySelector, comparer: null, descending: true);
+        }
+#endif
+
         public static IOrderedAsyncEnumerable<TSource> ThenByDescending<TSource, TKey>(this IOrderedAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
         {
             if (source == null)
@@ -167,5 +241,17 @@ namespace System.Linq
 
             return source.CreateOrderedEnumerable(keySelector, comparer, descending: true);
         }
+
+#if !NO_DEEP_CANCELLATION
+        public static IOrderedAsyncEnumerable<TSource> ThenByDescending<TSource, TKey>(this IOrderedAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, IComparer<TKey> comparer)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (keySelector == null)
+                throw Error.ArgumentNull(nameof(keySelector));
+
+            return source.CreateOrderedEnumerable(keySelector, comparer, descending: true);
+        }
+#endif
     }
 }

+ 80 - 6
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/OrderedAsyncEnumerable.cs

@@ -28,6 +28,11 @@ namespace System.Linq
             return new OrderedAsyncEnumerableWithTask<TElement, TKey>(_source, keySelector, comparer, descending, this);
         }
 
+        IOrderedAsyncEnumerable<TElement> IOrderedAsyncEnumerable<TElement>.CreateOrderedEnumerable<TKey>(Func<TElement, CancellationToken, ValueTask<TKey>> keySelector, IComparer<TKey> comparer, bool descending)
+        {
+            return new OrderedAsyncEnumerableWithTaskAndCancellation<TElement, TKey>(_source, keySelector, comparer, descending, this);
+        }
+
         protected override async ValueTask<bool> MoveNextCore()
         {
             switch (_state)
@@ -37,7 +42,7 @@ namespace System.Linq
 
                     // REVIEW: If we add selectors with CancellationToken support, we should feed the token to Sort.
 
-                    var sorter = GetAsyncEnumerableSorter(next: null);
+                    var sorter = GetAsyncEnumerableSorter(next: null, _cancellationToken);
                     _indexes = await sorter.Sort(_buffer, _buffer.Length).ConfigureAwait(false);
                     _index = 0;
 
@@ -66,7 +71,7 @@ namespace System.Linq
             await base.DisposeAsync().ConfigureAwait(false);
         }
 
-        internal abstract AsyncEnumerableSorter<TElement> GetAsyncEnumerableSorter(AsyncEnumerableSorter<TElement> next);
+        internal abstract AsyncEnumerableSorter<TElement> GetAsyncEnumerableSorter(AsyncEnumerableSorter<TElement> next, CancellationToken cancellationToken);
     }
 
     internal sealed class OrderedAsyncEnumerable<TElement, TKey> : OrderedAsyncEnumerable<TElement>
@@ -93,13 +98,13 @@ namespace System.Linq
             return new OrderedAsyncEnumerable<TElement, TKey>(_source, _keySelector, _comparer, _descending, _parent);
         }
 
-        internal override AsyncEnumerableSorter<TElement> GetAsyncEnumerableSorter(AsyncEnumerableSorter<TElement> next)
+        internal override AsyncEnumerableSorter<TElement> GetAsyncEnumerableSorter(AsyncEnumerableSorter<TElement> next, CancellationToken cancellationToken)
         {
             var sorter = new SyncKeySelectorAsyncEnumerableSorter<TElement, TKey>(_keySelector, _comparer, _descending, next);
 
             if (_parent != null)
             {
-                return _parent.GetAsyncEnumerableSorter(sorter);
+                return _parent.GetAsyncEnumerableSorter(sorter, cancellationToken);
             }
 
             return sorter;
@@ -130,19 +135,58 @@ namespace System.Linq
             return new OrderedAsyncEnumerableWithTask<TElement, TKey>(_source, _keySelector, _comparer, _descending, _parent);
         }
 
-        internal override AsyncEnumerableSorter<TElement> GetAsyncEnumerableSorter(AsyncEnumerableSorter<TElement> next)
+        internal override AsyncEnumerableSorter<TElement> GetAsyncEnumerableSorter(AsyncEnumerableSorter<TElement> next, CancellationToken cancellationToken)
         {
             var sorter = new AsyncKeySelectorAsyncEnumerableSorter<TElement, TKey>(_keySelector, _comparer, _descending, next);
 
             if (_parent != null)
             {
-                return _parent.GetAsyncEnumerableSorter(sorter);
+                return _parent.GetAsyncEnumerableSorter(sorter, cancellationToken);
             }
 
             return sorter;
         }
     }
 
+#if !NO_DEEP_CANCELLATION
+    internal sealed class OrderedAsyncEnumerableWithTaskAndCancellation<TElement, TKey> : OrderedAsyncEnumerable<TElement>
+    {
+        private readonly IComparer<TKey> _comparer;
+        private readonly bool _descending;
+        private readonly Func<TElement, CancellationToken, ValueTask<TKey>> _keySelector;
+        private readonly OrderedAsyncEnumerable<TElement> _parent;
+
+        public OrderedAsyncEnumerableWithTaskAndCancellation(IAsyncEnumerable<TElement> source, Func<TElement, CancellationToken, ValueTask<TKey>> keySelector, IComparer<TKey> comparer, bool descending, OrderedAsyncEnumerable<TElement> parent)
+        {
+            Debug.Assert(source != null);
+            Debug.Assert(keySelector != null);
+
+            _source = source;
+            _keySelector = keySelector;
+            _comparer = comparer;
+            _descending = descending;
+            _parent = parent;
+        }
+
+        public override AsyncIteratorBase<TElement> Clone()
+        {
+            return new OrderedAsyncEnumerableWithTaskAndCancellation<TElement, TKey>(_source, _keySelector, _comparer, _descending, _parent);
+        }
+
+        internal override AsyncEnumerableSorter<TElement> GetAsyncEnumerableSorter(AsyncEnumerableSorter<TElement> next, CancellationToken cancellationToken)
+        {
+            var sorter = new AsyncKeySelectorAsyncEnumerableSorterWithCancellation<TElement, TKey>(_keySelector, _comparer, _descending, next, cancellationToken);
+
+            if (_parent != null)
+            {
+                return _parent.GetAsyncEnumerableSorter(sorter, cancellationToken);
+            }
+
+            return sorter;
+        }
+    }
+#endif
+
     internal abstract class AsyncEnumerableSorter<TElement>
     {
         internal abstract ValueTask ComputeKeys(TElement[] elements, int count);
@@ -305,4 +349,34 @@ namespace System.Linq
             }
         }
     }
+
+#if !NO_DEEP_CANCELLATION
+    internal sealed class AsyncKeySelectorAsyncEnumerableSorterWithCancellation<TElement, TKey> : AsyncEnumerableSorterBase<TElement, TKey>
+    {
+        private readonly Func<TElement, CancellationToken, ValueTask<TKey>> _keySelector;
+        private readonly CancellationToken _cancellationToken;
+
+        public AsyncKeySelectorAsyncEnumerableSorterWithCancellation(Func<TElement, CancellationToken, ValueTask<TKey>> keySelector, IComparer<TKey> comparer, bool descending, AsyncEnumerableSorter<TElement> next, CancellationToken cancellationToken)
+            : base(comparer, descending, next)
+        {
+            _keySelector = keySelector;
+            _cancellationToken = cancellationToken;
+        }
+
+        internal override async ValueTask ComputeKeys(TElement[] elements, int count)
+        {
+            _keys = new TKey[count];
+
+            for (var i = 0; i < count; i++)
+            {
+                _keys[i] = await _keySelector(elements[i], _cancellationToken).ConfigureAwait(false);
+            }
+
+            if (_next != null)
+            {
+                await _next.ComputeKeys(elements, count).ConfigureAwait(false);
+            }
+        }
+    }
+#endif
 }

+ 285 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Select.cs

@@ -57,6 +57,26 @@ namespace System.Linq
             return new SelectEnumerableAsyncIteratorWithTask<TSource, TResult>(source, selector);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IAsyncEnumerable<TResult> Select<TSource, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TResult>> selector)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            switch (source)
+            {
+                case AsyncIterator<TSource> iterator:
+                    return iterator.Select(selector);
+                case IList<TSource> list:
+                    return new SelectIListIteratorWithTaskAndCancellation<TSource, TResult>(list, selector);
+            }
+
+            return new SelectEnumerableAsyncIteratorWithTaskAndCancellation<TSource, TResult>(source, selector);
+        }
+#endif
+
         public static IAsyncEnumerable<TResult> Select<TSource, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, int, ValueTask<TResult>> selector)
         {
             if (source == null)
@@ -67,6 +87,18 @@ namespace System.Linq
             return new SelectEnumerableWithIndexAsyncIteratorWithTask<TSource, TResult>(source, selector);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IAsyncEnumerable<TResult> Select<TSource, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, ValueTask<TResult>> selector)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return new SelectEnumerableWithIndexAsyncIteratorWithTaskAndCancellation<TSource, TResult>(source, selector);
+        }
+#endif
+
         private static Func<TSource, TResult> CombineSelectors<TSource, TMiddle, TResult>(Func<TSource, TMiddle> selector1, Func<TMiddle, TResult> selector2)
         {
             return x => selector2(selector1(x));
@@ -77,6 +109,13 @@ namespace System.Linq
             return async x => await selector2(await selector1(x).ConfigureAwait(false)).ConfigureAwait(false);
         }
 
+#if !NO_DEEP_CANCELLATION
+        private static Func<TSource, CancellationToken, ValueTask<TResult>> CombineSelectors<TSource, TMiddle, TResult>(Func<TSource, CancellationToken, ValueTask<TMiddle>> selector1, Func<TMiddle, CancellationToken, ValueTask<TResult>> selector2)
+        {
+            return async (x, ct) => await selector2(await selector1(x, ct).ConfigureAwait(false), ct).ConfigureAwait(false);
+        }
+#endif
+
         internal sealed class SelectEnumerableAsyncIterator<TSource, TResult> : AsyncIterator<TResult>
         {
             private readonly Func<TSource, TResult> _selector;
@@ -373,6 +412,69 @@ namespace System.Linq
             }
         }
 
+#if !NO_DEEP_CANCELLATION
+        internal sealed class SelectEnumerableAsyncIteratorWithTaskAndCancellation<TSource, TResult> : AsyncIterator<TResult>
+        {
+            private readonly Func<TSource, CancellationToken, ValueTask<TResult>> _selector;
+            private readonly IAsyncEnumerable<TSource> _source;
+
+            private IAsyncEnumerator<TSource> _enumerator;
+
+            public SelectEnumerableAsyncIteratorWithTaskAndCancellation(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TResult>> selector)
+            {
+                Debug.Assert(source != null);
+                Debug.Assert(selector != null);
+
+                _source = source;
+                _selector = selector;
+            }
+
+            public override AsyncIteratorBase<TResult> Clone()
+            {
+                return new SelectEnumerableAsyncIteratorWithTaskAndCancellation<TSource, TResult>(_source, _selector);
+            }
+
+            public override async ValueTask DisposeAsync()
+            {
+                if (_enumerator != null)
+                {
+                    await _enumerator.DisposeAsync().ConfigureAwait(false);
+                    _enumerator = null;
+                }
+
+                await base.DisposeAsync().ConfigureAwait(false);
+            }
+
+            public override IAsyncEnumerable<TResult1> Select<TResult1>(Func<TResult, CancellationToken, ValueTask<TResult1>> selector)
+            {
+                return new SelectEnumerableAsyncIteratorWithTaskAndCancellation<TSource, TResult1>(_source, CombineSelectors(_selector, selector));
+            }
+
+            protected override async ValueTask<bool> MoveNextCore()
+            {
+                switch (_state)
+                {
+                    case AsyncIteratorState.Allocated:
+                        _enumerator = _source.GetAsyncEnumerator(_cancellationToken);
+                        _state = AsyncIteratorState.Iterating;
+                        goto case AsyncIteratorState.Iterating;
+
+                    case AsyncIteratorState.Iterating:
+                        if (await _enumerator.MoveNextAsync().ConfigureAwait(false))
+                        {
+                            _current = await _selector(_enumerator.Current, _cancellationToken).ConfigureAwait(false);
+                            return true;
+                        }
+
+                        break;
+                }
+
+                await DisposeAsync().ConfigureAwait(false);
+                return false;
+            }
+        }
+#endif
+
         internal sealed class SelectEnumerableWithIndexAsyncIteratorWithTask<TSource, TResult> : AsyncIterator<TResult>
         {
             private readonly Func<TSource, int, ValueTask<TResult>> _selector;
@@ -437,6 +539,72 @@ namespace System.Linq
             }
         }
 
+#if !NO_DEEP_CANCELLATION
+        internal sealed class SelectEnumerableWithIndexAsyncIteratorWithTaskAndCancellation<TSource, TResult> : AsyncIterator<TResult>
+        {
+            private readonly Func<TSource, int, CancellationToken, ValueTask<TResult>> _selector;
+            private readonly IAsyncEnumerable<TSource> _source;
+            private IAsyncEnumerator<TSource> _enumerator;
+            private int _index;
+
+            public SelectEnumerableWithIndexAsyncIteratorWithTaskAndCancellation(IAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, ValueTask<TResult>> selector)
+            {
+                Debug.Assert(source != null);
+                Debug.Assert(selector != null);
+
+                _source = source;
+                _selector = selector;
+            }
+
+            public override AsyncIteratorBase<TResult> Clone()
+            {
+                return new SelectEnumerableWithIndexAsyncIteratorWithTaskAndCancellation<TSource, TResult>(_source, _selector);
+            }
+
+            public override async ValueTask DisposeAsync()
+            {
+                if (_enumerator != null)
+                {
+                    await _enumerator.DisposeAsync().ConfigureAwait(false);
+                    _enumerator = null;
+                }
+
+                await base.DisposeAsync().ConfigureAwait(false);
+            }
+
+            protected override async ValueTask<bool> MoveNextCore()
+            {
+                switch (_state)
+                {
+                    case AsyncIteratorState.Allocated:
+                        _enumerator = _source.GetAsyncEnumerator(_cancellationToken);
+                        _index = -1;
+                        _state = AsyncIteratorState.Iterating;
+                        goto case AsyncIteratorState.Iterating;
+
+                    case AsyncIteratorState.Iterating:
+                        if (await _enumerator.MoveNextAsync().ConfigureAwait(false))
+                        {
+                            var item = _enumerator.Current;
+
+                            checked
+                            {
+                                _index++;
+                            }
+
+                            _current = await _selector(item, _index, _cancellationToken).ConfigureAwait(false);
+                            return true;
+                        }
+
+                        break;
+                }
+
+                await DisposeAsync().ConfigureAwait(false);
+                return false;
+            }
+        }
+#endif
+
         // NB: LINQ to Objects implements IPartition<TResult> for this. However, it seems incorrect to do so in a trivial
         //     manner where e.g. TryGetLast simply indexes into the list without running the selector for the first n - 1
         //     elements in order to ensure side-effects. We should consider whether we want to follow this implementation
@@ -556,5 +724,122 @@ namespace System.Linq
                 return false;
             }
         }
+
+#if !NO_DEEP_CANCELLATION
+        internal sealed class SelectIListIteratorWithTaskAndCancellation<TSource, TResult> : AsyncIterator<TResult>, IAsyncIListProvider<TResult>
+        {
+            private readonly Func<TSource, CancellationToken, ValueTask<TResult>> _selector;
+            private readonly IList<TSource> _source;
+            private IEnumerator<TSource> _enumerator;
+
+            public SelectIListIteratorWithTaskAndCancellation(IList<TSource> source, Func<TSource, CancellationToken, ValueTask<TResult>> selector)
+            {
+                Debug.Assert(source != null);
+                Debug.Assert(selector != null);
+
+                _source = source;
+                _selector = selector;
+            }
+
+            public override AsyncIteratorBase<TResult> Clone()
+            {
+                return new SelectIListIteratorWithTaskAndCancellation<TSource, TResult>(_source, _selector);
+            }
+
+            public override async ValueTask DisposeAsync()
+            {
+                if (_enumerator != null)
+                {
+                    _enumerator.Dispose();
+                    _enumerator = null;
+                }
+
+                await base.DisposeAsync().ConfigureAwait(false);
+            }
+
+            public Task<int> GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
+            {
+                if (onlyIfCheap)
+                {
+                    return TaskExt.MinusOne;
+                }
+
+                return Core();
+
+                async Task<int> Core()
+                {
+                    var count = 0;
+
+                    foreach (var item in _source)
+                    {
+                        await _selector(item, cancellationToken).ConfigureAwait(false);
+
+                        checked
+                        {
+                            count++;
+                        }
+                    }
+
+                    return count;
+                }
+            }
+
+            public override IAsyncEnumerable<TResult1> Select<TResult1>(Func<TResult, CancellationToken, ValueTask<TResult1>> selector)
+            {
+                return new SelectIListIteratorWithTaskAndCancellation<TSource, TResult1>(_source, CombineSelectors(_selector, selector));
+            }
+
+            public async Task<TResult[]> ToArrayAsync(CancellationToken cancellationToken)
+            {
+                var n = _source.Count;
+
+                var res = new TResult[n];
+
+                for (var i = 0; i < n; i++)
+                {
+                    res[i] = await _selector(_source[i], cancellationToken).ConfigureAwait(false);
+                }
+
+                return res;
+            }
+
+            public async Task<List<TResult>> ToListAsync(CancellationToken cancellationToken)
+            {
+                var n = _source.Count;
+
+                var res = new List<TResult>(n);
+
+                for (var i = 0; i < n; i++)
+                {
+                    res.Add(await _selector(_source[i], cancellationToken).ConfigureAwait(false));
+                }
+
+                return res;
+            }
+
+            protected override async ValueTask<bool> MoveNextCore()
+            {
+                switch (_state)
+                {
+                    case AsyncIteratorState.Allocated:
+                        _enumerator = _source.GetEnumerator();
+                        _state = AsyncIteratorState.Iterating;
+                        goto case AsyncIteratorState.Iterating;
+
+                    case AsyncIteratorState.Iterating:
+                        if (_enumerator.MoveNext())
+                        {
+                            _current = await _selector(_enumerator.Current, _cancellationToken).ConfigureAwait(false);
+                            return true;
+                        }
+
+                        break;
+                }
+
+                await DisposeAsync().ConfigureAwait(false);
+                return false;
+            }
+        }
+#endif
     }
 }

+ 453 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/SelectMany.cs

@@ -31,6 +31,18 @@ namespace System.Linq
             return new SelectManyAsyncIteratorWithTask<TSource, TResult>(source, selector);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IAsyncEnumerable<TResult> SelectMany<TSource, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<IAsyncEnumerable<TResult>>> selector)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return new SelectManyAsyncIteratorWithTaskAndCancellation<TSource, TResult>(source, selector);
+        }
+#endif
+
         public static IAsyncEnumerable<TResult> SelectMany<TSource, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, int, IAsyncEnumerable<TResult>> selector)
         {
             if (source == null)
@@ -51,6 +63,18 @@ namespace System.Linq
             return new SelectManyWithIndexAsyncIteratorWithTask<TSource, TResult>(source, selector);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IAsyncEnumerable<TResult> SelectMany<TSource, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, ValueTask<IAsyncEnumerable<TResult>>> selector)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return new SelectManyWithIndexAsyncIteratorWithTaskAndCancellation<TSource, TResult>(source, selector);
+        }
+#endif
+
         public static IAsyncEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, IAsyncEnumerable<TCollection>> selector, Func<TSource, TCollection, TResult> resultSelector)
         {
             if (source == null)
@@ -75,6 +99,20 @@ namespace System.Linq
             return new SelectManyAsyncIteratorWithTask<TSource, TCollection, TResult>(source, selector, resultSelector);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IAsyncEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<IAsyncEnumerable<TCollection>>> selector, Func<TSource, TCollection, CancellationToken, ValueTask<TResult>> resultSelector)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+            if (resultSelector == null)
+                throw Error.ArgumentNull(nameof(resultSelector));
+
+            return new SelectManyAsyncIteratorWithTaskAndCancellation<TSource, TCollection, TResult>(source, selector, resultSelector);
+        }
+#endif
+
         public static IAsyncEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, int, IAsyncEnumerable<TCollection>> selector, Func<TSource, TCollection, TResult> resultSelector)
         {
             if (source == null)
@@ -99,6 +137,20 @@ namespace System.Linq
             return new SelectManyWithIndexAsyncIteratorWithTask<TSource, TCollection, TResult>(source, selector, resultSelector);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IAsyncEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, ValueTask<IAsyncEnumerable<TCollection>>> selector, Func<TSource, TCollection, CancellationToken, ValueTask<TResult>> resultSelector)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+            if (resultSelector == null)
+                throw Error.ArgumentNull(nameof(resultSelector));
+
+            return new SelectManyWithIndexAsyncIteratorWithTaskAndCancellation<TSource, TCollection, TResult>(source, selector, resultSelector);
+        }
+#endif
+
         private sealed class SelectManyAsyncIterator<TSource, TResult> : AsyncIterator<TResult>
         {
             private const int State_Source = 1;
@@ -281,6 +333,99 @@ namespace System.Linq
             }
         }
 
+#if !NO_DEEP_CANCELLATION
+        private sealed class SelectManyAsyncIteratorWithTaskAndCancellation<TSource, TResult> : AsyncIterator<TResult>
+        {
+            private const int State_Source = 1;
+            private const int State_Result = 2;
+
+            private readonly Func<TSource, CancellationToken, ValueTask<IAsyncEnumerable<TResult>>> _selector;
+            private readonly IAsyncEnumerable<TSource> _source;
+
+            private int _mode;
+            private IAsyncEnumerator<TResult> _resultEnumerator;
+            private IAsyncEnumerator<TSource> _sourceEnumerator;
+
+            public SelectManyAsyncIteratorWithTaskAndCancellation(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<IAsyncEnumerable<TResult>>> selector)
+            {
+                Debug.Assert(source != null);
+                Debug.Assert(selector != null);
+
+                _source = source;
+                _selector = selector;
+            }
+
+            public override AsyncIteratorBase<TResult> Clone()
+            {
+                return new SelectManyAsyncIteratorWithTaskAndCancellation<TSource, TResult>(_source, _selector);
+            }
+
+            public override async ValueTask DisposeAsync()
+            {
+                if (_resultEnumerator != null)
+                {
+                    await _resultEnumerator.DisposeAsync().ConfigureAwait(false);
+                    _resultEnumerator = null;
+                }
+
+                if (_sourceEnumerator != null)
+                {
+                    await _sourceEnumerator.DisposeAsync().ConfigureAwait(false);
+                    _sourceEnumerator = null;
+                }
+
+                await base.DisposeAsync().ConfigureAwait(false);
+            }
+
+            protected override async ValueTask<bool> MoveNextCore()
+            {
+                switch (_state)
+                {
+                    case AsyncIteratorState.Allocated:
+                        _sourceEnumerator = _source.GetAsyncEnumerator(_cancellationToken);
+                        _mode = State_Source;
+                        _state = AsyncIteratorState.Iterating;
+                        goto case AsyncIteratorState.Iterating;
+
+                    case AsyncIteratorState.Iterating:
+                        switch (_mode)
+                        {
+                            case State_Source:
+                                if (await _sourceEnumerator.MoveNextAsync().ConfigureAwait(false))
+                                {
+                                    if (_resultEnumerator != null)
+                                    {
+                                        await _resultEnumerator.DisposeAsync().ConfigureAwait(false);
+                                    }
+
+                                    var inner = await _selector(_sourceEnumerator.Current, _cancellationToken).ConfigureAwait(false);
+                                    _resultEnumerator = inner.GetAsyncEnumerator(_cancellationToken);
+
+                                    _mode = State_Result;
+                                    goto case State_Result;
+                                }
+                                break;
+
+                            case State_Result:
+                                if (await _resultEnumerator.MoveNextAsync().ConfigureAwait(false))
+                                {
+                                    _current = _resultEnumerator.Current;
+                                    return true;
+                                }
+
+                                _mode = State_Source;
+                                goto case State_Source; // loop
+                        }
+
+                        break;
+                }
+
+                await DisposeAsync().ConfigureAwait(false);
+                return false;
+            }
+        }
+#endif
+
         private sealed class SelectManyAsyncIterator<TSource, TCollection, TResult> : AsyncIterator<TResult>
         {
             private const int State_Source = 1;
@@ -477,6 +622,106 @@ namespace System.Linq
             }
         }
 
+#if !NO_DEEP_CANCELLATION
+        private sealed class SelectManyAsyncIteratorWithTaskAndCancellation<TSource, TCollection, TResult> : AsyncIterator<TResult>
+        {
+            private const int State_Source = 1;
+            private const int State_Result = 2;
+
+            private readonly Func<TSource, CancellationToken, ValueTask<IAsyncEnumerable<TCollection>>> _collectionSelector;
+            private readonly Func<TSource, TCollection, CancellationToken, ValueTask<TResult>> _resultSelector;
+            private readonly IAsyncEnumerable<TSource> _source;
+
+            private TSource _currentSource;
+            private int _mode;
+            private IAsyncEnumerator<TCollection> _resultEnumerator;
+            private IAsyncEnumerator<TSource> _sourceEnumerator;
+
+            public SelectManyAsyncIteratorWithTaskAndCancellation(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<IAsyncEnumerable<TCollection>>> collectionSelector, Func<TSource, TCollection, CancellationToken, ValueTask<TResult>> resultSelector)
+            {
+                Debug.Assert(source != null);
+                Debug.Assert(collectionSelector != null);
+                Debug.Assert(resultSelector != null);
+
+                _source = source;
+                _collectionSelector = collectionSelector;
+                _resultSelector = resultSelector;
+            }
+
+            public override AsyncIteratorBase<TResult> Clone()
+            {
+                return new SelectManyAsyncIteratorWithTaskAndCancellation<TSource, TCollection, TResult>(_source, _collectionSelector, _resultSelector);
+            }
+
+            public override async ValueTask DisposeAsync()
+            {
+                if (_resultEnumerator != null)
+                {
+                    await _resultEnumerator.DisposeAsync().ConfigureAwait(false);
+                    _resultEnumerator = null;
+                }
+
+                if (_sourceEnumerator != null)
+                {
+                    await _sourceEnumerator.DisposeAsync().ConfigureAwait(false);
+                    _sourceEnumerator = null;
+                }
+
+                _currentSource = default;
+
+                await base.DisposeAsync().ConfigureAwait(false);
+            }
+
+            protected override async ValueTask<bool> MoveNextCore()
+            {
+                switch (_state)
+                {
+                    case AsyncIteratorState.Allocated:
+                        _sourceEnumerator = _source.GetAsyncEnumerator(_cancellationToken);
+                        _mode = State_Source;
+                        _state = AsyncIteratorState.Iterating;
+                        goto case AsyncIteratorState.Iterating;
+
+                    case AsyncIteratorState.Iterating:
+                        switch (_mode)
+                        {
+                            case State_Source:
+                                if (await _sourceEnumerator.MoveNextAsync().ConfigureAwait(false))
+                                {
+                                    if (_resultEnumerator != null)
+                                    {
+                                        await _resultEnumerator.DisposeAsync().ConfigureAwait(false);
+                                    }
+
+                                    _currentSource = _sourceEnumerator.Current;
+                                    var inner = await _collectionSelector(_currentSource, _cancellationToken).ConfigureAwait(false);
+                                    _resultEnumerator = inner.GetAsyncEnumerator(_cancellationToken);
+
+                                    _mode = State_Result;
+                                    goto case State_Result;
+                                }
+                                break;
+
+                            case State_Result:
+                                if (await _resultEnumerator.MoveNextAsync().ConfigureAwait(false))
+                                {
+                                    _current = await _resultSelector(_currentSource, _resultEnumerator.Current, _cancellationToken).ConfigureAwait(false);
+                                    return true;
+                                }
+
+                                _mode = State_Source;
+                                goto case State_Source; // loop
+                        }
+
+                        break;
+                }
+
+                await DisposeAsync().ConfigureAwait(false);
+                return false;
+            }
+        }
+#endif
+
         private sealed class SelectManyWithIndexAsyncIterator<TSource, TCollection, TResult> : AsyncIterator<TResult>
         {
             private const int State_Source = 1;
@@ -689,6 +934,114 @@ namespace System.Linq
             }
         }
 
+#if !NO_DEEP_CANCELLATION
+        private sealed class SelectManyWithIndexAsyncIteratorWithTaskAndCancellation<TSource, TCollection, TResult> : AsyncIterator<TResult>
+        {
+            private const int State_Source = 1;
+            private const int State_Result = 2;
+
+            private readonly Func<TSource, int, CancellationToken, ValueTask<IAsyncEnumerable<TCollection>>> _collectionSelector;
+            private readonly Func<TSource, TCollection, CancellationToken, ValueTask<TResult>> _resultSelector;
+            private readonly IAsyncEnumerable<TSource> _source;
+
+            private TSource _currentSource;
+            private int _index;
+            private int _mode;
+            private IAsyncEnumerator<TCollection> _resultEnumerator;
+            private IAsyncEnumerator<TSource> _sourceEnumerator;
+
+            public SelectManyWithIndexAsyncIteratorWithTaskAndCancellation(IAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, ValueTask<IAsyncEnumerable<TCollection>>> collectionSelector, Func<TSource, TCollection, CancellationToken, ValueTask<TResult>> resultSelector)
+            {
+                Debug.Assert(source != null);
+                Debug.Assert(collectionSelector != null);
+                Debug.Assert(resultSelector != null);
+
+                _source = source;
+                _collectionSelector = collectionSelector;
+                _resultSelector = resultSelector;
+            }
+
+            public override AsyncIteratorBase<TResult> Clone()
+            {
+                return new SelectManyWithIndexAsyncIteratorWithTaskAndCancellation<TSource, TCollection, TResult>(_source, _collectionSelector, _resultSelector);
+            }
+
+            public override async ValueTask DisposeAsync()
+            {
+                if (_resultEnumerator != null)
+                {
+                    await _resultEnumerator.DisposeAsync().ConfigureAwait(false);
+                    _resultEnumerator = null;
+                }
+
+                if (_sourceEnumerator != null)
+                {
+                    await _sourceEnumerator.DisposeAsync().ConfigureAwait(false);
+                    _sourceEnumerator = null;
+                }
+
+                _currentSource = default;
+
+                await base.DisposeAsync().ConfigureAwait(false);
+            }
+
+            protected override async ValueTask<bool> MoveNextCore()
+            {
+                switch (_state)
+                {
+                    case AsyncIteratorState.Allocated:
+                        _sourceEnumerator = _source.GetAsyncEnumerator(_cancellationToken);
+                        _index = -1;
+                        _mode = State_Source;
+                        _state = AsyncIteratorState.Iterating;
+                        goto case AsyncIteratorState.Iterating;
+
+                    case AsyncIteratorState.Iterating:
+                        switch (_mode)
+                        {
+                            case State_Source:
+                                if (await _sourceEnumerator.MoveNextAsync().ConfigureAwait(false))
+                                {
+                                    if (_resultEnumerator != null)
+                                    {
+                                        await _resultEnumerator.DisposeAsync().ConfigureAwait(false);
+                                    }
+
+                                    _currentSource = _sourceEnumerator.Current;
+
+                                    checked
+                                    {
+                                        _index++;
+                                    }
+
+                                    var inner = await _collectionSelector(_currentSource, _index, _cancellationToken).ConfigureAwait(false);
+                                    _resultEnumerator = inner.GetAsyncEnumerator(_cancellationToken);
+
+                                    _mode = State_Result;
+                                    goto case State_Result;
+                                }
+                                break;
+
+                            case State_Result:
+                                if (await _resultEnumerator.MoveNextAsync().ConfigureAwait(false))
+                                {
+                                    _current = await _resultSelector(_currentSource, _resultEnumerator.Current, _cancellationToken).ConfigureAwait(false);
+                                    return true;
+                                }
+
+                                _mode = State_Source;
+                                goto case State_Source; // loop
+                        }
+
+                        break;
+                }
+
+                await DisposeAsync().ConfigureAwait(false);
+                return false;
+            }
+        }
+#endif
+
         private sealed class SelectManyWithIndexAsyncIterator<TSource, TResult> : AsyncIterator<TResult>
         {
             private const int State_Source = 1;
@@ -884,5 +1237,105 @@ namespace System.Linq
                 return false;
             }
         }
+
+#if !NO_DEEP_CANCELLATION
+        private sealed class SelectManyWithIndexAsyncIteratorWithTaskAndCancellation<TSource, TResult> : AsyncIterator<TResult>
+        {
+            private const int State_Source = 1;
+            private const int State_Result = 2;
+
+            private readonly Func<TSource, int, CancellationToken, ValueTask<IAsyncEnumerable<TResult>>> _selector;
+            private readonly IAsyncEnumerable<TSource> _source;
+
+            private int _index;
+            private int _mode;
+            private IAsyncEnumerator<TResult> _resultEnumerator;
+            private IAsyncEnumerator<TSource> _sourceEnumerator;
+
+            public SelectManyWithIndexAsyncIteratorWithTaskAndCancellation(IAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, ValueTask<IAsyncEnumerable<TResult>>> selector)
+            {
+                Debug.Assert(source != null);
+                Debug.Assert(selector != null);
+
+                _source = source;
+                _selector = selector;
+            }
+
+            public override AsyncIteratorBase<TResult> Clone()
+            {
+                return new SelectManyWithIndexAsyncIteratorWithTaskAndCancellation<TSource, TResult>(_source, _selector);
+            }
+
+            public override async ValueTask DisposeAsync()
+            {
+                if (_resultEnumerator != null)
+                {
+                    await _resultEnumerator.DisposeAsync().ConfigureAwait(false);
+                    _resultEnumerator = null;
+                }
+
+                if (_sourceEnumerator != null)
+                {
+                    await _sourceEnumerator.DisposeAsync().ConfigureAwait(false);
+                    _sourceEnumerator = null;
+                }
+
+                await base.DisposeAsync().ConfigureAwait(false);
+            }
+
+            protected override async ValueTask<bool> MoveNextCore()
+            {
+                switch (_state)
+                {
+                    case AsyncIteratorState.Allocated:
+                        _sourceEnumerator = _source.GetAsyncEnumerator(_cancellationToken);
+                        _index = -1;
+                        _mode = State_Source;
+                        _state = AsyncIteratorState.Iterating;
+                        goto case AsyncIteratorState.Iterating;
+
+                    case AsyncIteratorState.Iterating:
+                        switch (_mode)
+                        {
+                            case State_Source:
+                                if (await _sourceEnumerator.MoveNextAsync().ConfigureAwait(false))
+                                {
+                                    if (_resultEnumerator != null)
+                                    {
+                                        await _resultEnumerator.DisposeAsync().ConfigureAwait(false);
+                                    }
+
+                                    checked
+                                    {
+                                        _index++;
+                                    }
+
+                                    var inner = await _selector(_sourceEnumerator.Current, _index, _cancellationToken).ConfigureAwait(false);
+                                    _resultEnumerator = inner.GetAsyncEnumerator(_cancellationToken);
+
+                                    _mode = State_Result;
+                                    goto case State_Result;
+                                }
+                                break;
+
+                            case State_Result:
+                                if (await _resultEnumerator.MoveNextAsync().ConfigureAwait(false))
+                                {
+                                    _current = _resultEnumerator.Current;
+                                    return true;
+                                }
+
+                                _mode = State_Source;
+                                goto case State_Source; // loop
+                        }
+
+                        break;
+                }
+
+                await DisposeAsync().ConfigureAwait(false);
+                return false;
+            }
+        }
+#endif
     }
 }

+ 46 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Single.cs

@@ -66,6 +66,18 @@ namespace System.Linq
             return SingleCore(source, predicate, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<TSource> SingleAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (predicate == null)
+                throw Error.ArgumentNull(nameof(predicate));
+
+            return SingleCore(source, predicate, cancellationToken);
+        }
+#endif
+
         private static async Task<TSource> SingleCore<TSource>(IAsyncEnumerable<TSource> source, CancellationToken cancellationToken)
         {
             if (source is IList<TSource> list)
@@ -165,5 +177,39 @@ namespace System.Linq
                 await e.DisposeAsync().ConfigureAwait(false);
             }
         }
+
+#if !NO_DEEP_CANCELLATION
+        private static async Task<TSource> SingleCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate, CancellationToken cancellationToken)
+        {
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var result = e.Current;
+
+                    if (await predicate(result, cancellationToken).ConfigureAwait(false))
+                    {
+                        while (await e.MoveNextAsync().ConfigureAwait(false))
+                        {
+                            if (await predicate(e.Current, cancellationToken).ConfigureAwait(false))
+                            {
+                                throw Error.MoreThanOneElement();
+                            }
+                        }
+
+                        return result;
+                    }
+                }
+
+                throw Error.NoElements();
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+        }
+#endif
     }
 }

+ 46 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/SingleOrDefault.cs

@@ -66,6 +66,18 @@ namespace System.Linq
             return SingleOrDefaultCore(source, predicate, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<TSource> SingleOrDefaultAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (predicate == null)
+                throw Error.ArgumentNull(nameof(predicate));
+
+            return SingleOrDefaultCore(source, predicate, cancellationToken);
+        }
+#endif
+
         private static async Task<TSource> SingleOrDefaultCore<TSource>(IAsyncEnumerable<TSource> source, CancellationToken cancellationToken)
         {
             if (source is IList<TSource> list)
@@ -166,5 +178,39 @@ namespace System.Linq
                 await e.DisposeAsync().ConfigureAwait(false);
             }
         }
+
+#if !NO_DEEP_CANCELLATION
+        private static async Task<TSource> SingleOrDefaultCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate, CancellationToken cancellationToken)
+        {
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var result = e.Current;
+
+                    if (await predicate(result, cancellationToken).ConfigureAwait(false))
+                    {
+                        while (await e.MoveNextAsync().ConfigureAwait(false))
+                        {
+                            if (await predicate(e.Current, cancellationToken).ConfigureAwait(false))
+                            {
+                                throw Error.MoreThanOneElement();
+                            }
+                        }
+
+                        return result;
+                    }
+                }
+
+                return default;
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+        }
+#endif
     }
 }

+ 188 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/SkipWhile.cs

@@ -41,6 +41,18 @@ namespace System.Linq
             return new SkipWhileAsyncIteratorWithTask<TSource>(source, predicate);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IAsyncEnumerable<TSource> SkipWhile<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (predicate == null)
+                throw Error.ArgumentNull(nameof(predicate));
+
+            return new SkipWhileAsyncIteratorWithTaskAndCancellation<TSource>(source, predicate);
+        }
+#endif
+
         public static IAsyncEnumerable<TSource> SkipWhile<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, int, ValueTask<bool>> predicate)
         {
             if (source == null)
@@ -51,6 +63,18 @@ namespace System.Linq
             return new SkipWhileWithIndexAsyncIteratorWithTask<TSource>(source, predicate);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IAsyncEnumerable<TSource> SkipWhile<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, ValueTask<bool>> predicate)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (predicate == null)
+                throw Error.ArgumentNull(nameof(predicate));
+
+            return new SkipWhileWithIndexAsyncIteratorWithTaskAndCancellation<TSource>(source, predicate);
+        }
+#endif
+
         private sealed class SkipWhileAsyncIterator<TSource> : AsyncIterator<TSource>
         {
             private readonly Func<TSource, bool> _predicate;
@@ -287,6 +311,84 @@ namespace System.Linq
             }
         }
 
+#if !NO_DEEP_CANCELLATION
+        private sealed class SkipWhileAsyncIteratorWithTaskAndCancellation<TSource> : AsyncIterator<TSource>
+        {
+            private readonly Func<TSource, CancellationToken, ValueTask<bool>> _predicate;
+            private readonly IAsyncEnumerable<TSource> _source;
+
+            private bool _doMoveNext;
+            private IAsyncEnumerator<TSource> _enumerator;
+
+            public SkipWhileAsyncIteratorWithTaskAndCancellation(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate)
+            {
+                Debug.Assert(predicate != null);
+                Debug.Assert(source != null);
+
+                _source = source;
+                _predicate = predicate;
+            }
+
+            public override AsyncIteratorBase<TSource> Clone()
+            {
+                return new SkipWhileAsyncIteratorWithTaskAndCancellation<TSource>(_source, _predicate);
+            }
+
+            public override async ValueTask DisposeAsync()
+            {
+                if (_enumerator != null)
+                {
+                    await _enumerator.DisposeAsync().ConfigureAwait(false);
+                    _enumerator = null;
+                }
+
+                await base.DisposeAsync().ConfigureAwait(false);
+            }
+
+            protected override async ValueTask<bool> MoveNextCore()
+            {
+                switch (_state)
+                {
+                    case AsyncIteratorState.Allocated:
+                        _enumerator = _source.GetAsyncEnumerator(_cancellationToken);
+
+                        // skip elements as requested
+                        while (await _enumerator.MoveNextAsync().ConfigureAwait(false))
+                        {
+                            var element = _enumerator.Current;
+                            if (!await _predicate(element, _cancellationToken).ConfigureAwait(false))
+                            {
+                                _doMoveNext = false;
+                                _state = AsyncIteratorState.Iterating;
+                                goto case AsyncIteratorState.Iterating;
+                            }
+                        }
+
+                        break;
+
+                    case AsyncIteratorState.Iterating:
+                        if (_doMoveNext && await _enumerator.MoveNextAsync().ConfigureAwait(false))
+                        {
+                            _current = _enumerator.Current;
+                            return true;
+                        }
+
+                        if (!_doMoveNext)
+                        {
+                            _current = _enumerator.Current;
+                            _doMoveNext = true;
+                            return true;
+                        }
+
+                        break;
+                }
+
+                await DisposeAsync().ConfigureAwait(false);
+                return false;
+            }
+        }
+#endif
+
         private sealed class SkipWhileWithIndexAsyncIteratorWithTask<TSource> : AsyncIterator<TSource>
         {
             private readonly Func<TSource, int, ValueTask<bool>> _predicate;
@@ -370,5 +472,91 @@ namespace System.Linq
                 return false;
             }
         }
+
+#if !NO_DEEP_CANCELLATION
+        private sealed class SkipWhileWithIndexAsyncIteratorWithTaskAndCancellation<TSource> : AsyncIterator<TSource>
+        {
+            private readonly Func<TSource, int, CancellationToken, ValueTask<bool>> _predicate;
+            private readonly IAsyncEnumerable<TSource> _source;
+
+            private bool _doMoveNext;
+            private IAsyncEnumerator<TSource> _enumerator;
+            private int _index;
+
+            public SkipWhileWithIndexAsyncIteratorWithTaskAndCancellation(IAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, ValueTask<bool>> predicate)
+            {
+                Debug.Assert(predicate != null);
+                Debug.Assert(source != null);
+
+                _source = source;
+                _predicate = predicate;
+            }
+
+            public override AsyncIteratorBase<TSource> Clone()
+            {
+                return new SkipWhileWithIndexAsyncIteratorWithTaskAndCancellation<TSource>(_source, _predicate);
+            }
+
+            public override async ValueTask DisposeAsync()
+            {
+                if (_enumerator != null)
+                {
+                    await _enumerator.DisposeAsync().ConfigureAwait(false);
+                    _enumerator = null;
+                }
+
+                await base.DisposeAsync().ConfigureAwait(false);
+            }
+
+            protected override async ValueTask<bool> MoveNextCore()
+            {
+                switch (_state)
+                {
+                    case AsyncIteratorState.Allocated:
+                        _enumerator = _source.GetAsyncEnumerator(_cancellationToken);
+                        _index = -1;
+
+                        // skip elements as requested
+                        while (await _enumerator.MoveNextAsync().ConfigureAwait(false))
+                        {
+                            var element = _enumerator.Current;
+
+                            checked
+                            {
+                                _index++;
+                            }
+
+                            if (!await _predicate(element, _index, _cancellationToken).ConfigureAwait(false))
+                            {
+                                _doMoveNext = false;
+                                _state = AsyncIteratorState.Iterating;
+                                goto case AsyncIteratorState.Iterating;
+                            }
+                        }
+
+                        break;
+
+                    case AsyncIteratorState.Iterating:
+                        if (_doMoveNext && await _enumerator.MoveNextAsync().ConfigureAwait(false))
+                        {
+                            _current = _enumerator.Current;
+                            return true;
+                        }
+
+                        if (!_doMoveNext)
+                        {
+                            _current = _enumerator.Current;
+                            _doMoveNext = true;
+                            return true;
+                        }
+
+                        break;
+                }
+
+                await DisposeAsync().ConfigureAwait(false);
+                return false;
+            }
+        }
+#endif
     }
 }

+ 382 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Sum.Generated.cs

@@ -66,6 +66,18 @@ namespace System.Linq
             return SumCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<int> SumAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<int>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return SumCore(source, selector, cancellationToken);
+        }
+#endif
+
         private static async Task<int> SumCore(IAsyncEnumerable<int> source, CancellationToken cancellationToken)
         {
             var sum = 0;
@@ -142,6 +154,34 @@ namespace System.Linq
             return sum;
         }
 
+#if !NO_DEEP_CANCELLATION
+        private static async Task<int> SumCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<int>> selector, CancellationToken cancellationToken)
+        {
+            var sum = 0;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+
+                    checked
+                    {
+                        sum += value;
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return sum;
+        }
+#endif
+
         public static Task<long> SumAsync(this IAsyncEnumerable<long> source)
         {
             if (source == null)
@@ -198,6 +238,18 @@ namespace System.Linq
             return SumCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<long> SumAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<long>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return SumCore(source, selector, cancellationToken);
+        }
+#endif
+
         private static async Task<long> SumCore(IAsyncEnumerable<long> source, CancellationToken cancellationToken)
         {
             var sum = 0L;
@@ -274,6 +326,34 @@ namespace System.Linq
             return sum;
         }
 
+#if !NO_DEEP_CANCELLATION
+        private static async Task<long> SumCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<long>> selector, CancellationToken cancellationToken)
+        {
+            var sum = 0L;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+
+                    checked
+                    {
+                        sum += value;
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return sum;
+        }
+#endif
+
         public static Task<float> SumAsync(this IAsyncEnumerable<float> source)
         {
             if (source == null)
@@ -330,6 +410,18 @@ namespace System.Linq
             return SumCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<float> SumAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<float>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return SumCore(source, selector, cancellationToken);
+        }
+#endif
+
         private static async Task<float> SumCore(IAsyncEnumerable<float> source, CancellationToken cancellationToken)
         {
             var sum = 0.0f;
@@ -397,6 +489,31 @@ namespace System.Linq
             return sum;
         }
 
+#if !NO_DEEP_CANCELLATION
+        private static async Task<float> SumCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<float>> selector, CancellationToken cancellationToken)
+        {
+            var sum = 0.0f;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+
+                    sum += value;
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return sum;
+        }
+#endif
+
         public static Task<double> SumAsync(this IAsyncEnumerable<double> source)
         {
             if (source == null)
@@ -453,6 +570,18 @@ namespace System.Linq
             return SumCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<double> SumAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<double>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return SumCore(source, selector, cancellationToken);
+        }
+#endif
+
         private static async Task<double> SumCore(IAsyncEnumerable<double> source, CancellationToken cancellationToken)
         {
             var sum = 0.0;
@@ -520,6 +649,31 @@ namespace System.Linq
             return sum;
         }
 
+#if !NO_DEEP_CANCELLATION
+        private static async Task<double> SumCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<double>> selector, CancellationToken cancellationToken)
+        {
+            var sum = 0.0;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+
+                    sum += value;
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return sum;
+        }
+#endif
+
         public static Task<decimal> SumAsync(this IAsyncEnumerable<decimal> source)
         {
             if (source == null)
@@ -576,6 +730,18 @@ namespace System.Linq
             return SumCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<decimal> SumAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<decimal>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return SumCore(source, selector, cancellationToken);
+        }
+#endif
+
         private static async Task<decimal> SumCore(IAsyncEnumerable<decimal> source, CancellationToken cancellationToken)
         {
             var sum = 0m;
@@ -643,6 +809,31 @@ namespace System.Linq
             return sum;
         }
 
+#if !NO_DEEP_CANCELLATION
+        private static async Task<decimal> SumCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<decimal>> selector, CancellationToken cancellationToken)
+        {
+            var sum = 0m;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+
+                    sum += value;
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return sum;
+        }
+#endif
+
         public static Task<int?> SumAsync(this IAsyncEnumerable<int?> source)
         {
             if (source == null)
@@ -699,6 +890,18 @@ namespace System.Linq
             return SumCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<int?> SumAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<int?>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return SumCore(source, selector, cancellationToken);
+        }
+#endif
+
         private static async Task<int?> SumCore(IAsyncEnumerable<int?> source, CancellationToken cancellationToken)
         {
             var sum = 0;
@@ -775,6 +978,34 @@ namespace System.Linq
             return sum;
         }
 
+#if !NO_DEEP_CANCELLATION
+        private static async Task<int?> SumCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<int?>> selector, CancellationToken cancellationToken)
+        {
+            var sum = 0;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+
+                    checked
+                    {
+                        sum += value.GetValueOrDefault();
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return sum;
+        }
+#endif
+
         public static Task<long?> SumAsync(this IAsyncEnumerable<long?> source)
         {
             if (source == null)
@@ -831,6 +1062,18 @@ namespace System.Linq
             return SumCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<long?> SumAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<long?>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return SumCore(source, selector, cancellationToken);
+        }
+#endif
+
         private static async Task<long?> SumCore(IAsyncEnumerable<long?> source, CancellationToken cancellationToken)
         {
             var sum = 0L;
@@ -907,6 +1150,34 @@ namespace System.Linq
             return sum;
         }
 
+#if !NO_DEEP_CANCELLATION
+        private static async Task<long?> SumCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<long?>> selector, CancellationToken cancellationToken)
+        {
+            var sum = 0L;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+
+                    checked
+                    {
+                        sum += value.GetValueOrDefault();
+                    }
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return sum;
+        }
+#endif
+
         public static Task<float?> SumAsync(this IAsyncEnumerable<float?> source)
         {
             if (source == null)
@@ -963,6 +1234,18 @@ namespace System.Linq
             return SumCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<float?> SumAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<float?>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return SumCore(source, selector, cancellationToken);
+        }
+#endif
+
         private static async Task<float?> SumCore(IAsyncEnumerable<float?> source, CancellationToken cancellationToken)
         {
             var sum = 0.0f;
@@ -1030,6 +1313,31 @@ namespace System.Linq
             return sum;
         }
 
+#if !NO_DEEP_CANCELLATION
+        private static async Task<float?> SumCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<float?>> selector, CancellationToken cancellationToken)
+        {
+            var sum = 0.0f;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+
+                    sum += value.GetValueOrDefault();
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return sum;
+        }
+#endif
+
         public static Task<double?> SumAsync(this IAsyncEnumerable<double?> source)
         {
             if (source == null)
@@ -1086,6 +1394,18 @@ namespace System.Linq
             return SumCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<double?> SumAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<double?>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return SumCore(source, selector, cancellationToken);
+        }
+#endif
+
         private static async Task<double?> SumCore(IAsyncEnumerable<double?> source, CancellationToken cancellationToken)
         {
             var sum = 0.0;
@@ -1153,6 +1473,31 @@ namespace System.Linq
             return sum;
         }
 
+#if !NO_DEEP_CANCELLATION
+        private static async Task<double?> SumCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<double?>> selector, CancellationToken cancellationToken)
+        {
+            var sum = 0.0;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+
+                    sum += value.GetValueOrDefault();
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return sum;
+        }
+#endif
+
         public static Task<decimal?> SumAsync(this IAsyncEnumerable<decimal?> source)
         {
             if (source == null)
@@ -1209,6 +1554,18 @@ namespace System.Linq
             return SumCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<decimal?> SumAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<decimal?>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return SumCore(source, selector, cancellationToken);
+        }
+#endif
+
         private static async Task<decimal?> SumCore(IAsyncEnumerable<decimal?> source, CancellationToken cancellationToken)
         {
             var sum = 0m;
@@ -1276,5 +1633,30 @@ namespace System.Linq
             return sum;
         }
 
+#if !NO_DEEP_CANCELLATION
+        private static async Task<decimal?> SumCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<decimal?>> selector, CancellationToken cancellationToken)
+        {
+            var sum = 0m;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+
+                    sum += value.GetValueOrDefault();
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return sum;
+        }
+#endif
+
     }
 }

+ 53 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Sum.Generated.tt

@@ -91,6 +91,18 @@ foreach (var o in os)
             return SumCore(source, selector, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<<#=o.type#>> SumAsync<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<<#=o.type#>>> selector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return SumCore(source, selector, cancellationToken);
+        }
+#endif
+
         private static async Task<<#=o.type#>> SumCore(IAsyncEnumerable<<#=o.type#>> source, CancellationToken cancellationToken)
         {
             var sum = <#=o.zero#>;
@@ -206,6 +218,47 @@ else
             return sum;
         }
 
+#if !NO_DEEP_CANCELLATION
+        private static async Task<<#=o.type#>> SumCore<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<<#=o.type#>>> selector, CancellationToken cancellationToken)
+        {
+            var sum = <#=o.zero#>;
+
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var value = await selector(e.Current, cancellationToken).ConfigureAwait(false);
+
+<#
+if (o.@checked)
+{
+#>
+                    checked
+                    {
+                        sum += value<#=n#>;
+                    }
+<#
+}
+else
+{
+#>
+                    sum += value<#=n#>;
+<#
+}
+#>
+                }
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+
+            return sum;
+        }
+#endif
+
 <#
 }
 #>

+ 163 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/TakeWhile.cs

@@ -41,6 +41,18 @@ namespace System.Linq
             return new TakeWhileAsyncIteratorWithTask<TSource>(source, predicate);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IAsyncEnumerable<TSource> TakeWhile<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (predicate == null)
+                throw Error.ArgumentNull(nameof(predicate));
+
+            return new TakeWhileAsyncIteratorWithTaskAndCancellation<TSource>(source, predicate);
+        }
+#endif
+
         public static IAsyncEnumerable<TSource> TakeWhile<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, int, ValueTask<bool>> predicate)
         {
             if (source == null)
@@ -51,6 +63,18 @@ namespace System.Linq
             return new TakeWhileWithIndexAsyncIteratorWithTask<TSource>(source, predicate);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IAsyncEnumerable<TSource> TakeWhile<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, ValueTask<bool>> predicate)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (predicate == null)
+                throw Error.ArgumentNull(nameof(predicate));
+
+            return new TakeWhileWithIndexAsyncIteratorWithTaskAndCancellation<TSource>(source, predicate);
+        }
+#endif
+
         private sealed class TakeWhileAsyncIterator<TSource> : AsyncIterator<TSource>
         {
             private readonly Func<TSource, bool> _predicate;
@@ -250,6 +274,72 @@ namespace System.Linq
             }
         }
 
+#if !NO_DEEP_CANCELLATION
+        private sealed class TakeWhileAsyncIteratorWithTaskAndCancellation<TSource> : AsyncIterator<TSource>
+        {
+            private readonly Func<TSource, CancellationToken, ValueTask<bool>> _predicate;
+            private readonly IAsyncEnumerable<TSource> _source;
+
+            private IAsyncEnumerator<TSource> _enumerator;
+
+            public TakeWhileAsyncIteratorWithTaskAndCancellation(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate)
+            {
+                Debug.Assert(predicate != null);
+                Debug.Assert(source != null);
+
+                _source = source;
+                _predicate = predicate;
+            }
+
+            public override AsyncIteratorBase<TSource> Clone()
+            {
+                return new TakeWhileAsyncIteratorWithTaskAndCancellation<TSource>(_source, _predicate);
+            }
+
+            public override async ValueTask DisposeAsync()
+            {
+                if (_enumerator != null)
+                {
+                    await _enumerator.DisposeAsync().ConfigureAwait(false);
+                    _enumerator = null;
+                }
+
+                await base.DisposeAsync().ConfigureAwait(false);
+            }
+
+            protected override async ValueTask<bool> MoveNextCore()
+            {
+                switch (_state)
+                {
+                    case AsyncIteratorState.Allocated:
+                        _enumerator = _source.GetAsyncEnumerator(_cancellationToken);
+
+                        _state = AsyncIteratorState.Iterating;
+                        goto case AsyncIteratorState.Iterating;
+
+
+                    case AsyncIteratorState.Iterating:
+                        if (await _enumerator.MoveNextAsync().ConfigureAwait(false))
+                        {
+                            var item = _enumerator.Current;
+                            if (!await _predicate(item, _cancellationToken).ConfigureAwait(false))
+                            {
+                                break;
+                            }
+
+                            _current = item;
+                            return true;
+                        }
+
+                        break;
+                }
+
+                await DisposeAsync().ConfigureAwait(false);
+                return false;
+            }
+        }
+#endif
+
         private sealed class TakeWhileWithIndexAsyncIteratorWithTask<TSource> : AsyncIterator<TSource>
         {
             private readonly Func<TSource, int, ValueTask<bool>> _predicate;
@@ -320,5 +410,78 @@ namespace System.Linq
                 return false;
             }
         }
+
+#if !NO_DEEP_CANCELLATION
+        private sealed class TakeWhileWithIndexAsyncIteratorWithTaskAndCancellation<TSource> : AsyncIterator<TSource>
+        {
+            private readonly Func<TSource, int, CancellationToken, ValueTask<bool>> _predicate;
+            private readonly IAsyncEnumerable<TSource> _source;
+
+            private IAsyncEnumerator<TSource> _enumerator;
+            private int _index;
+
+            public TakeWhileWithIndexAsyncIteratorWithTaskAndCancellation(IAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, ValueTask<bool>> predicate)
+            {
+                Debug.Assert(predicate != null);
+                Debug.Assert(source != null);
+
+                _source = source;
+                _predicate = predicate;
+            }
+
+            public override AsyncIteratorBase<TSource> Clone()
+            {
+                return new TakeWhileWithIndexAsyncIteratorWithTaskAndCancellation<TSource>(_source, _predicate);
+            }
+
+            public override async ValueTask DisposeAsync()
+            {
+                if (_enumerator != null)
+                {
+                    await _enumerator.DisposeAsync().ConfigureAwait(false);
+                    _enumerator = null;
+                }
+
+                await base.DisposeAsync().ConfigureAwait(false);
+            }
+
+            protected override async ValueTask<bool> MoveNextCore()
+            {
+                switch (_state)
+                {
+                    case AsyncIteratorState.Allocated:
+                        _enumerator = _source.GetAsyncEnumerator(_cancellationToken);
+                        _index = -1;
+                        _state = AsyncIteratorState.Iterating;
+                        goto case AsyncIteratorState.Iterating;
+
+
+                    case AsyncIteratorState.Iterating:
+                        if (await _enumerator.MoveNextAsync().ConfigureAwait(false))
+                        {
+                            var item = _enumerator.Current;
+
+                            checked
+                            {
+                                _index++;
+                            }
+
+                            if (!await _predicate(item, _index, _cancellationToken).ConfigureAwait(false))
+                            {
+                                break;
+                            }
+
+                            _current = item;
+                            return true;
+                        }
+
+                        break;
+                }
+
+                await DisposeAsync().ConfigureAwait(false);
+                return false;
+            }
+        }
+#endif
     }
 }

+ 80 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/ToDictionary.cs

@@ -50,6 +50,18 @@ namespace System.Linq
             return ToDictionaryCore(source, keySelector, x => new ValueTask<TSource>(x), EqualityComparer<TKey>.Default, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<Dictionary<TKey, TSource>> ToDictionaryAsync<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (keySelector == null)
+                throw Error.ArgumentNull(nameof(keySelector));
+
+            return ToDictionaryCore(source, x => keySelector(x, cancellationToken), x => new ValueTask<TSource>(x), EqualityComparer<TKey>.Default, cancellationToken);
+        }
+#endif
+
         public static Task<Dictionary<TKey, TSource>> ToDictionaryAsync<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
         {
             if (source == null)
@@ -90,6 +102,18 @@ namespace System.Linq
             return ToDictionaryCore(source, keySelector, x => new ValueTask<TSource>(x), comparer, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<Dictionary<TKey, TSource>> ToDictionaryAsync<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (keySelector == null)
+                throw Error.ArgumentNull(nameof(keySelector));
+
+            return ToDictionaryCore(source, x => keySelector(x, cancellationToken), x => new ValueTask<TSource>(x), comparer, cancellationToken);
+        }
+#endif
+
         public static Task<Dictionary<TKey, TElement>> ToDictionaryAsync<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector)
         {
             if (source == null)
@@ -138,6 +162,20 @@ namespace System.Linq
             return ToDictionaryCore(source, keySelector, elementSelector, EqualityComparer<TKey>.Default, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<Dictionary<TKey, TElement>> ToDictionaryAsync<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TSource, CancellationToken, ValueTask<TElement>> elementSelector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (keySelector == null)
+                throw Error.ArgumentNull(nameof(keySelector));
+            if (elementSelector == null)
+                throw Error.ArgumentNull(nameof(elementSelector));
+
+            return ToDictionaryCore(source, keySelector, elementSelector, EqualityComparer<TKey>.Default, cancellationToken);
+        }
+#endif
+
         public static Task<Dictionary<TKey, TElement>> ToDictionaryAsync<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer)
         {
             if (source == null)
@@ -186,6 +224,20 @@ namespace System.Linq
             return ToDictionaryCore(source, keySelector, elementSelector, comparer, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<Dictionary<TKey, TElement>> ToDictionaryAsync<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TSource, CancellationToken, ValueTask<TElement>> elementSelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (keySelector == null)
+                throw Error.ArgumentNull(nameof(keySelector));
+            if (elementSelector == null)
+                throw Error.ArgumentNull(nameof(elementSelector));
+
+            return ToDictionaryCore(source, keySelector, elementSelector, comparer, cancellationToken);
+        }
+#endif
+
         private static async Task<Dictionary<TKey, TElement>> ToDictionaryCore<TSource, TKey, TElement>(IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
         {
             var e = source.GetAsyncEnumerator(cancellationToken);
@@ -237,5 +289,33 @@ namespace System.Linq
                 await e.DisposeAsync().ConfigureAwait(false);
             }
         }
+
+#if !NO_DEEP_CANCELLATION
+        private static async Task<Dictionary<TKey, TElement>> ToDictionaryCore<TSource, TKey, TElement>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TSource, CancellationToken, ValueTask<TElement>> elementSelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
+        {
+            var e = source.GetAsyncEnumerator(cancellationToken);
+
+            try
+            {
+                var d = new Dictionary<TKey, TElement>(comparer);
+
+                while (await e.MoveNextAsync().ConfigureAwait(false))
+                {
+                    var x = e.Current;
+
+                    var key = await keySelector(x, cancellationToken).ConfigureAwait(false);
+                    var value = await elementSelector(x, cancellationToken).ConfigureAwait(false);
+
+                    d.Add(key, value);
+                }
+
+                return d;
+            }
+            finally
+            {
+                await e.DisposeAsync().ConfigureAwait(false);
+            }
+        }
+#endif
     }
 }

+ 59 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/ToLookup.cs

@@ -50,6 +50,18 @@ namespace System.Linq
             return ToLookupCore<TSource, TKey, TSource>(source, keySelector, x => new ValueTask<TSource>(x), comparer: null, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<ILookup<TKey, TSource>> ToLookupAsync<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (keySelector == null)
+                throw Error.ArgumentNull(nameof(keySelector));
+
+            return ToLookupCore<TSource, TKey, TSource>(source, x => keySelector(x, cancellationToken), x => new ValueTask<TSource>(x), comparer: null, cancellationToken);
+        }
+#endif
+
         public static Task<ILookup<TKey, TSource>> ToLookupAsync<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
         {
             if (source == null)
@@ -90,6 +102,18 @@ namespace System.Linq
             return ToLookupCore(source, keySelector, x => new ValueTask<TSource>(x), comparer, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<ILookup<TKey, TSource>> ToLookupAsync<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (keySelector == null)
+                throw Error.ArgumentNull(nameof(keySelector));
+
+            return ToLookupCore(source, x => keySelector(x, cancellationToken), x => new ValueTask<TSource>(x), comparer, cancellationToken);
+        }
+#endif
+
         public static Task<ILookup<TKey, TElement>> ToLookupAsync<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector)
         {
             if (source == null)
@@ -138,6 +162,20 @@ namespace System.Linq
             return ToLookupCore<TSource, TKey, TElement>(source, keySelector, elementSelector, comparer: null, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<ILookup<TKey, TElement>> ToLookupAsync<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TSource, CancellationToken, ValueTask<TElement>> elementSelector, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (keySelector == null)
+                throw Error.ArgumentNull(nameof(keySelector));
+            if (elementSelector == null)
+                throw Error.ArgumentNull(nameof(elementSelector));
+
+            return ToLookupCore<TSource, TKey, TElement>(source, keySelector, elementSelector, comparer: null, cancellationToken);
+        }
+#endif
+
         public static Task<ILookup<TKey, TElement>> ToLookupAsync<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer)
         {
             if (source == null)
@@ -186,6 +224,20 @@ namespace System.Linq
             return ToLookupCore(source, keySelector, elementSelector, comparer, cancellationToken);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static Task<ILookup<TKey, TElement>> ToLookupAsync<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TSource, CancellationToken, ValueTask<TElement>> elementSelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (keySelector == null)
+                throw Error.ArgumentNull(nameof(keySelector));
+            if (elementSelector == null)
+                throw Error.ArgumentNull(nameof(elementSelector));
+
+            return ToLookupCore(source, keySelector, elementSelector, comparer, cancellationToken);
+        }
+#endif
+
         private static async Task<ILookup<TKey, TElement>> ToLookupCore<TSource, TKey, TElement>(IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
         {
             return await Internal.Lookup<TKey, TElement>.CreateAsync(source, keySelector, elementSelector, comparer, cancellationToken).ConfigureAwait(false);
@@ -195,5 +247,12 @@ namespace System.Linq
         {
             return await Internal.LookupWithTask<TKey, TElement>.CreateAsync(source, keySelector, elementSelector, comparer, cancellationToken).ConfigureAwait(false);
         }
+
+#if !NO_DEEP_CANCELLATION
+        private static async Task<ILookup<TKey, TElement>> ToLookupCore<TSource, TKey, TElement>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TSource, CancellationToken, ValueTask<TElement>> elementSelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
+        {
+            return await Internal.LookupWithTask<TKey, TElement>.CreateAsync(source, keySelector, elementSelector, comparer, cancellationToken).ConfigureAwait(false);
+        }
+#endif
     }
 }

+ 174 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Where.cs

@@ -4,6 +4,7 @@
 
 using System.Collections.Generic;
 using System.Diagnostics;
+using System.Threading;
 using System.Threading.Tasks;
 
 namespace System.Linq
@@ -52,6 +53,24 @@ namespace System.Linq
             return new WhereEnumerableAsyncIteratorWithTask<TSource>(source, predicate);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IAsyncEnumerable<TSource> Where<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (predicate == null)
+                throw Error.ArgumentNull(nameof(predicate));
+
+            if (source is AsyncIteratorBase<TSource> iterator)
+            {
+                return iterator.Where(predicate);
+            }
+
+            // TODO: Can we add array/list optimizations here, does it make sense?
+            return new WhereEnumerableAsyncIteratorWithTaskAndCancellation<TSource>(source, predicate);
+        }
+#endif
+
         public static IAsyncEnumerable<TSource> Where<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, int, ValueTask<bool>> predicate)
         {
             if (source == null)
@@ -62,6 +81,18 @@ namespace System.Linq
             return new WhereEnumerableWithIndexAsyncIteratorWithTask<TSource>(source, predicate);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IAsyncEnumerable<TSource> Where<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, ValueTask<bool>> predicate)
+        {
+            if (source == null)
+                throw Error.ArgumentNull(nameof(source));
+            if (predicate == null)
+                throw Error.ArgumentNull(nameof(predicate));
+
+            return new WhereEnumerableWithIndexAsyncIteratorWithTaskAndCancellation<TSource>(source, predicate);
+        }
+#endif
+
         private static Func<TSource, bool> CombinePredicates<TSource>(Func<TSource, bool> predicate1, Func<TSource, bool> predicate2)
         {
             return x => predicate1(x) && predicate2(x);
@@ -72,6 +103,13 @@ namespace System.Linq
             return async x => await predicate1(x).ConfigureAwait(false) && await predicate2(x).ConfigureAwait(false);
         }
 
+#if !NO_DEEP_CANCELLATION
+        private static Func<TSource, CancellationToken, ValueTask<bool>> CombinePredicates<TSource>(Func<TSource, CancellationToken, ValueTask<bool>> predicate1, Func<TSource, CancellationToken, ValueTask<bool>> predicate2)
+        {
+            return async (x, ct) => await predicate1(x, ct).ConfigureAwait(false) && await predicate2(x, ct).ConfigureAwait(false);
+        }
+#endif
+
         internal sealed class WhereEnumerableAsyncIterator<TSource> : AsyncIterator<TSource>
         {
             private readonly Func<TSource, bool> _predicate;
@@ -273,6 +311,72 @@ namespace System.Linq
             }
         }
 
+#if !NO_DEEP_CANCELLATION
+        internal sealed class WhereEnumerableAsyncIteratorWithTaskAndCancellation<TSource> : AsyncIterator<TSource>
+        {
+            private readonly Func<TSource, CancellationToken, ValueTask<bool>> _predicate;
+            private readonly IAsyncEnumerable<TSource> _source;
+            private IAsyncEnumerator<TSource> _enumerator;
+
+            public WhereEnumerableAsyncIteratorWithTaskAndCancellation(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate)
+            {
+                Debug.Assert(source != null);
+                Debug.Assert(predicate != null);
+
+                _source = source;
+                _predicate = predicate;
+            }
+
+            public override AsyncIteratorBase<TSource> Clone()
+            {
+                return new WhereEnumerableAsyncIteratorWithTaskAndCancellation<TSource>(_source, _predicate);
+            }
+
+            public override async ValueTask DisposeAsync()
+            {
+                if (_enumerator != null)
+                {
+                    await _enumerator.DisposeAsync().ConfigureAwait(false);
+                    _enumerator = null;
+                }
+
+                await base.DisposeAsync().ConfigureAwait(false);
+            }
+
+            public override IAsyncEnumerable<TSource> Where(Func<TSource, CancellationToken, ValueTask<bool>> predicate)
+            {
+                return new WhereEnumerableAsyncIteratorWithTaskAndCancellation<TSource>(_source, CombinePredicates(_predicate, predicate));
+            }
+
+            protected override async ValueTask<bool> MoveNextCore()
+            {
+                switch (_state)
+                {
+                    case AsyncIteratorState.Allocated:
+                        _enumerator = _source.GetAsyncEnumerator(_cancellationToken);
+                        _state = AsyncIteratorState.Iterating;
+                        goto case AsyncIteratorState.Iterating;
+
+                    case AsyncIteratorState.Iterating:
+                        while (await _enumerator.MoveNextAsync().ConfigureAwait(false))
+                        {
+                            var item = _enumerator.Current;
+                            if (await _predicate(item, _cancellationToken).ConfigureAwait(false))
+                            {
+                                _current = item;
+                                return true;
+                            }
+                        }
+
+                        await DisposeAsync().ConfigureAwait(false);
+                        break;
+                }
+
+                return false;
+            }
+        }
+#endif
+
         internal sealed class WhereEnumerableWithIndexAsyncIteratorWithTask<TSource> : AsyncIterator<TSource>
         {
             private readonly Func<TSource, int, ValueTask<bool>> _predicate;
@@ -341,6 +445,76 @@ namespace System.Linq
             }
         }
 
+#if !NO_DEEP_CANCELLATION
+        internal sealed class WhereEnumerableWithIndexAsyncIteratorWithTaskAndCancellation<TSource> : AsyncIterator<TSource>
+        {
+            private readonly Func<TSource, int, CancellationToken, ValueTask<bool>> _predicate;
+            private readonly IAsyncEnumerable<TSource> _source;
+
+            private IAsyncEnumerator<TSource> _enumerator;
+            private int _index;
+
+            public WhereEnumerableWithIndexAsyncIteratorWithTaskAndCancellation(IAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, ValueTask<bool>> predicate)
+            {
+                Debug.Assert(source != null);
+                Debug.Assert(predicate != null);
+
+                _source = source;
+                _predicate = predicate;
+            }
+
+            public override AsyncIteratorBase<TSource> Clone()
+            {
+                return new WhereEnumerableWithIndexAsyncIteratorWithTaskAndCancellation<TSource>(_source, _predicate);
+            }
+
+            public override async ValueTask DisposeAsync()
+            {
+                if (_enumerator != null)
+                {
+                    await _enumerator.DisposeAsync().ConfigureAwait(false);
+                    _enumerator = null;
+                }
+
+                await base.DisposeAsync().ConfigureAwait(false);
+            }
+
+            protected override async ValueTask<bool> MoveNextCore()
+            {
+                switch (_state)
+                {
+                    case AsyncIteratorState.Allocated:
+                        _enumerator = _source.GetAsyncEnumerator(_cancellationToken);
+                        _index = -1;
+                        _state = AsyncIteratorState.Iterating;
+                        goto case AsyncIteratorState.Iterating;
+
+                    case AsyncIteratorState.Iterating:
+                        while (await _enumerator.MoveNextAsync().ConfigureAwait(false))
+                        {
+                            var item = _enumerator.Current;
+
+                            checked
+                            {
+                                _index++;
+                            }
+
+                            if (await _predicate(item, _index, _cancellationToken).ConfigureAwait(false))
+                            {
+                                _current = item;
+                                return true;
+                            }
+                        }
+
+                        await DisposeAsync().ConfigureAwait(false);
+                        break;
+                }
+
+                return false;
+            }
+        }
+#endif
+
         internal sealed class WhereSelectEnumerableAsyncIterator<TSource, TResult> : AsyncIterator<TResult>
         {
             private readonly Func<TSource, bool> _predicate;

+ 88 - 0
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Zip.cs

@@ -35,6 +35,20 @@ namespace System.Linq
             return new ZipAsyncIteratorWithTask<TFirst, TSecond, TResult>(first, second, selector);
         }
 
+#if !NO_DEEP_CANCELLATION
+        public static IAsyncEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IAsyncEnumerable<TFirst> first, IAsyncEnumerable<TSecond> second, Func<TFirst, TSecond, CancellationToken, ValueTask<TResult>> selector)
+        {
+            if (first == null)
+                throw Error.ArgumentNull(nameof(first));
+            if (second == null)
+                throw Error.ArgumentNull(nameof(second));
+            if (selector == null)
+                throw Error.ArgumentNull(nameof(selector));
+
+            return new ZipAsyncIteratorWithTaskAndCancellation<TFirst, TSecond, TResult>(first, second, selector);
+        }
+#endif
+
         private sealed class ZipAsyncIterator<TFirst, TSecond, TResult> : AsyncIterator<TResult>
         {
             private readonly IAsyncEnumerable<TFirst> _first;
@@ -178,5 +192,79 @@ namespace System.Linq
                 return false;
             }
         }
+
+#if !NO_DEEP_CANCELLATION
+        private sealed class ZipAsyncIteratorWithTaskAndCancellation<TFirst, TSecond, TResult> : AsyncIterator<TResult>
+        {
+            private readonly IAsyncEnumerable<TFirst> _first;
+            private readonly IAsyncEnumerable<TSecond> _second;
+            private readonly Func<TFirst, TSecond, CancellationToken, ValueTask<TResult>> _selector;
+
+            private IAsyncEnumerator<TFirst> _firstEnumerator;
+            private IAsyncEnumerator<TSecond> _secondEnumerator;
+
+            public ZipAsyncIteratorWithTaskAndCancellation(IAsyncEnumerable<TFirst> first, IAsyncEnumerable<TSecond> second, Func<TFirst, TSecond, CancellationToken, ValueTask<TResult>> selector)
+            {
+                Debug.Assert(first != null);
+                Debug.Assert(second != null);
+                Debug.Assert(selector != null);
+
+                _first = first;
+                _second = second;
+                _selector = selector;
+            }
+
+            public override AsyncIteratorBase<TResult> Clone()
+            {
+                return new ZipAsyncIteratorWithTaskAndCancellation<TFirst, TSecond, TResult>(_first, _second, _selector);
+            }
+
+            public override async ValueTask DisposeAsync()
+            {
+                if (_secondEnumerator != null)
+                {
+                    await _secondEnumerator.DisposeAsync().ConfigureAwait(false);
+                    _secondEnumerator = null;
+                }
+
+                if (_firstEnumerator != null)
+                {
+                    await _firstEnumerator.DisposeAsync().ConfigureAwait(false);
+                    _firstEnumerator = null;
+                }
+
+                await base.DisposeAsync().ConfigureAwait(false);
+            }
+
+            protected override async ValueTask<bool> MoveNextCore()
+            {
+                // REVIEW: Earlier versions of this operator performed concurrent MoveNextAsync calls, which isn't a great default and
+                //         results in an unexpected source of concurrency. However, a concurrent Zip may be a worthy addition to the
+                //         API or System.Interactive.Async as a complementary implementation besides the conservative default.
+
+                switch (_state)
+                {
+                    case AsyncIteratorState.Allocated:
+                        _firstEnumerator = _first.GetAsyncEnumerator(_cancellationToken);
+                        _secondEnumerator = _second.GetAsyncEnumerator(_cancellationToken);
+
+                        _state = AsyncIteratorState.Iterating;
+                        goto case AsyncIteratorState.Iterating;
+
+                    case AsyncIteratorState.Iterating:
+                        if (await _firstEnumerator.MoveNextAsync().ConfigureAwait(false) && await _secondEnumerator.MoveNextAsync().ConfigureAwait(false))
+                        {
+                            _current = await _selector(_firstEnumerator.Current, _secondEnumerator.Current, _cancellationToken).ConfigureAwait(false);
+                            return true;
+                        }
+
+                        await DisposeAsync().ConfigureAwait(false);
+                        break;
+                }
+
+                return false;
+            }
+        }
+#endif
     }
 }