< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 86
Coverable lines: 86
Total lines: 176
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 27
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
OnTryRead(...)0%220%
Add(TElement& modreq(...)100%110%
CreateCollection(...)100%110%
OnTryWrite(...)0%220%
OnWriteResume(...)0%21210%
.ctor()100%110%
GetAsyncEnumerator()0%220%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableOfTConverter.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Collections.Generic;
 5using System.Diagnostics;
 6using System.Threading;
 7using System.Threading.Tasks;
 8
 9namespace System.Text.Json.Serialization.Converters
 10{
 11    internal sealed class IAsyncEnumerableOfTConverter<TAsyncEnumerable, TElement>
 12        : JsonCollectionConverter<TAsyncEnumerable, TElement>
 13        where TAsyncEnumerable : IAsyncEnumerable<TElement>
 14    {
 15        internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, s
 016        {
 017            if (!typeToConvert.IsAssignableFrom(typeof(IAsyncEnumerable<TElement>)))
 018            {
 019                ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(Type, ref reader, ref state);
 20            }
 21
 022            return base.OnTryRead(ref reader, typeToConvert, options, ref state, out value!);
 023        }
 24
 25        protected override void Add(in TElement value, ref ReadStack state)
 026        {
 027            ((BufferedAsyncEnumerable)state.Current.ReturnValue!)._buffer.Add(value);
 028        }
 29
 030        internal override bool SupportsCreateObjectDelegate => false;
 31        protected override void CreateCollection(ref Utf8JsonReader reader, scoped ref ReadStack state, JsonSerializerOp
 032        {
 033            state.Current.ReturnValue = new BufferedAsyncEnumerable();
 034        }
 35
 36        internal override bool OnTryWrite(Utf8JsonWriter writer, TAsyncEnumerable value, JsonSerializerOptions options, 
 037        {
 038            if (!state.SupportAsync)
 039            {
 040                ThrowHelper.ThrowNotSupportedException_TypeRequiresAsyncSerialization(Type);
 41            }
 42
 043            return base.OnTryWrite(writer, value, options, ref state);
 044        }
 45
 46        [Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2012:Use ValueTasks correctly", Justification = "Con
 47        protected override bool OnWriteResume(Utf8JsonWriter writer, TAsyncEnumerable value, JsonSerializerOptions optio
 048        {
 49            IAsyncEnumerator<TElement> enumerator;
 50            ValueTask<bool> moveNextTask;
 51
 052            switch (state.Current.AsyncEnumeratorState)
 53            {
 54                case AsyncEnumeratorState.None:
 055                    enumerator = value.GetAsyncEnumerator(state.CancellationToken);
 56                    // async enumerators can only be disposed asynchronously;
 57                    // store in the WriteStack for disposal on exception.
 058                    state.Current.AsyncEnumerator = enumerator;
 059                    state.Current.AsyncEnumeratorState = AsyncEnumeratorState.Enumerating;
 60                    // enumerator.MoveNextAsync() calls can throw,
 61                    // ensure the enumerator already is stored
 62                    // in the WriteStack for proper disposal.
 063                    moveNextTask = enumerator.MoveNextAsync();
 64
 065                    if (!moveNextTask.IsCompleted)
 066                    {
 67                        // It is common for first-time MoveNextAsync() calls to return pending tasks,
 68                        // since typically that is when underlying network connections are being established.
 69                        // For this case only, suppress flushing the current buffer contents (e.g. the leading '[' token
 70                        // to give the stream owner the ability to recover in case of a connection error.
 071                        state.SuppressFlush = true;
 072                        goto SuspendDueToPendingTask;
 73                    }
 074                    break;
 75
 76                case AsyncEnumeratorState.PendingMoveNext:
 077                    Debug.Assert(state.Current.AsyncEnumerator is IAsyncEnumerator<TElement>);
 078                    enumerator = (IAsyncEnumerator<TElement>)state.Current.AsyncEnumerator;
 79
 80                    // converter was previously suspended due to a pending MoveNextAsync() task
 081                    Debug.Assert(state.PendingTask is Task<bool> && state.PendingTask.IsCompleted);
 082                    moveNextTask = new ValueTask<bool>((Task<bool>)state.PendingTask);
 083                    state.Current.AsyncEnumeratorState = AsyncEnumeratorState.Enumerating;
 084                    state.PendingTask = null;
 085                    break;
 86
 87                case AsyncEnumeratorState.PendingDisposal:
 88                    // Converter was previously suspended due to a pending DisposeAsync() task.
 089                    Debug.Assert(state.Current.AsyncEnumerator is null);
 090                    Debug.Assert(state.PendingTask is not null && state.PendingTask.IsCompleted);
 091                    state.PendingTask.GetAwaiter().GetResult();
 092                    state.Current.AsyncEnumeratorState = AsyncEnumeratorState.None;
 093                    state.PendingTask = null;
 094                    return true;
 95
 96                case AsyncEnumeratorState.Enumerating:
 097                    Debug.Assert(state.Current.AsyncEnumerator is IAsyncEnumerator<TElement>);
 098                    enumerator = (IAsyncEnumerator<TElement>)state.Current.AsyncEnumerator;
 99
 100                    // converter was suspended for a different reason;
 101                    // the last MoveNextAsync() call can only have completed with 'true'.
 0102                    moveNextTask = new ValueTask<bool>(true);
 0103                    break;
 104
 105                default:
 0106                    throw new InvalidOperationException("Invalid async enumerator state.");
 107            }
 108
 0109            Debug.Assert(moveNextTask.IsCompleted);
 0110            JsonConverter<TElement> converter = GetElementConverter(ref state);
 111
 112            // iterate through the enumerator while elements are being returned synchronously
 113            do
 0114            {
 0115                if (!moveNextTask.Result)
 0116                {
 117                    // Enumeration complete, dispose the enumerator inline.
 118                    // Clear from the stack first to prevent double disposal on exception.
 0119                    state.Current.AsyncEnumerator = null;
 0120                    state.Current.AsyncEnumeratorState = AsyncEnumeratorState.None;
 0121                    ValueTask disposeTask = enumerator.DisposeAsync();
 0122                    if (!disposeTask.IsCompleted)
 0123                    {
 124                        // DisposeAsync is pending; store as a pending task
 125                        // and yield control to the root-level async serialization loop.
 0126                        state.PendingTask = disposeTask.AsTask();
 0127                        state.Current.AsyncEnumeratorState = AsyncEnumeratorState.PendingDisposal;
 0128                        return false;
 129                    }
 130
 0131                    disposeTask.GetAwaiter().GetResult();
 0132                    return true;
 133                }
 134
 0135                if (ShouldFlush(ref state, writer))
 0136                {
 0137                    return false;
 138                }
 139
 0140                TElement element = enumerator.Current;
 0141                if (!converter.TryWrite(writer, element, options, ref state))
 0142                {
 0143                    return false;
 144                }
 145
 0146                state.Current.EndCollectionElement();
 0147                moveNextTask = enumerator.MoveNextAsync();
 0148            } while (moveNextTask.IsCompleted);
 149
 0150        SuspendDueToPendingTask:
 151            // we have a pending MoveNextAsync() call;
 152            // wrap inside a regular task so that it can be awaited multiple times;
 153            // mark the current stackframe as pending completion.
 0154            Debug.Assert(state.PendingTask is null);
 0155            state.PendingTask = moveNextTask.AsTask();
 0156            state.Current.AsyncEnumeratorState = AsyncEnumeratorState.PendingMoveNext;
 0157            return false;
 0158        }
 159
 160        private sealed class BufferedAsyncEnumerable : IAsyncEnumerable<TElement>
 161        {
 0162            public readonly List<TElement> _buffer = new();
 163
 164#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
 165            public async IAsyncEnumerator<TElement> GetAsyncEnumerator(CancellationToken _)
 0166            {
 0167                foreach (TElement element in _buffer)
 0168                {
 0169                    yield return element;
 0170                }
 0171            }
 172#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
 173        }
 174    }
 175}
 176