Fix Assert in ValueTask (#17511)
[platform/upstream/coreclr.git] / src / mscorlib / shared / System / Threading / Tasks / ValueTask.cs
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 // See the LICENSE file in the project root for more information.
4
5 using System.Collections.Generic;
6 using System.Diagnostics;
7 using System.Runtime.CompilerServices;
8 using System.Runtime.InteropServices;
9 using System.Threading.Tasks.Sources;
10
11 #if !netstandard
12 using Internal.Runtime.CompilerServices;
13 #endif
14
15 namespace System.Threading.Tasks
16 {
17     // TYPE SAFETY WARNING:
18     // This code uses Unsafe.As to cast _obj.  This is done in order to minimize the costs associated with
19     // casting _obj to a variety of different types that can be stored in a ValueTask, e.g. Task<TResult>
20     // vs IValueTaskSource<TResult>.  Previous attempts at this were faulty due to using a separate field
21     // to store information about the type of the object in _obj; this is faulty because if the ValueTask
22     // is stored into a field, concurrent read/writes can result in tearing the _obj from the type information
23     // stored in a separate field.  This means we can rely only on the _obj field to determine how to handle
24     // it.  As such, the pattern employed is to copy _obj into a local obj, and then check it for null and
25     // type test against Task/Task<TResult>.  Since the ValueTask can only be constructed with null, Task,
26     // or IValueTaskSource, we can then be confident in knowing that if it doesn't match one of those values,
27     // it must be an IValueTaskSource, and we can use Unsafe.As.  This could be defeated by other unsafe means,
28     // like private reflection or using Unsafe.As manually, but at that point you're already doing things
29     // that can violate type safety; we only care about getting correct behaviors when using "safe" code.
30     // There are still other race conditions in user's code that can result in errors, but such errors don't
31     // cause ValueTask to violate type safety.
32
33     /// <summary>Provides an awaitable result of an asynchronous operation.</summary>
34     /// <remarks>
35     /// <see cref="ValueTask"/>s are meant to be directly awaited.  To do more complicated operations with them, a <see cref="Task"/>
36     /// should be extracted using <see cref="AsTask"/>.  Such operations might include caching an instance to be awaited later,
37     /// registering multiple continuations with a single operation, awaiting the same task multiple times, and using combinators over
38     /// multiple operations.
39     /// </remarks>
40     [AsyncMethodBuilder(typeof(AsyncValueTaskMethodBuilder))]
41     [StructLayout(LayoutKind.Auto)]
42     public readonly struct ValueTask : IEquatable<ValueTask>
43     {
44         /// <summary>A task canceled using `new CancellationToken(true)`.</summary>
45         private static readonly Task s_canceledTask =
46 #if netstandard
47             Task.Delay(Timeout.Infinite, new CancellationToken(canceled: true));
48 #else
49             Task.FromCanceled(new CancellationToken(canceled: true));
50 #endif
51         /// <summary>A successfully completed task.</summary>
52         internal static Task CompletedTask
53 #if netstandard
54             { get; } = Task.Delay(0);
55 #else
56             => Task.CompletedTask;
57 #endif
58
59         /// <summary>null if representing a successful synchronous completion, otherwise a <see cref="Task"/> or a <see cref="IValueTaskSource"/>.</summary>
60         internal readonly object _obj;
61         /// <summary>Opaque value passed through to the <see cref="IValueTaskSource"/>.</summary>
62         internal readonly short _token;
63         /// <summary>true to continue on the capture context; otherwise, true.</summary>
64         /// <remarks>Stored in the <see cref="ValueTask"/> rather than in the configured awaiter to utilize otherwise padding space.</remarks>
65         internal readonly bool _continueOnCapturedContext;
66
67         // An instance created with the default ctor (a zero init'd struct) represents a synchronously, successfully completed operation.
68
69         /// <summary>Initialize the <see cref="ValueTask"/> with a <see cref="Task"/> that represents the operation.</summary>
70         /// <param name="task">The task.</param>
71         [MethodImpl(MethodImplOptions.AggressiveInlining)]
72         public ValueTask(Task task)
73         {
74             if (task == null)
75             {
76                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.task);
77             }
78
79             _obj = task;
80
81             _continueOnCapturedContext = true;
82             _token = 0;
83         }
84
85         /// <summary>Initialize the <see cref="ValueTask"/> with a <see cref="IValueTaskSource"/> object that represents the operation.</summary>
86         /// <param name="source">The source.</param>
87         /// <param name="token">Opaque value passed through to the <see cref="IValueTaskSource"/>.</param>
88         [MethodImpl(MethodImplOptions.AggressiveInlining)]
89         public ValueTask(IValueTaskSource source, short token)
90         {
91             if (source == null)
92             {
93                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
94             }
95
96             _obj = source;
97             _token = token;
98
99             _continueOnCapturedContext = true;
100         }
101
102         [MethodImpl(MethodImplOptions.AggressiveInlining)]
103         private ValueTask(object obj, short token, bool continueOnCapturedContext)
104         {
105             _obj = obj;
106             _token = token;
107             _continueOnCapturedContext = continueOnCapturedContext;
108         }
109
110         /// <summary>Returns the hash code for this instance.</summary>
111         public override int GetHashCode() => _obj?.GetHashCode() ?? 0;
112
113         /// <summary>Returns a value indicating whether this value is equal to a specified <see cref="object"/>.</summary>
114         public override bool Equals(object obj) =>
115             obj is ValueTask &&
116             Equals((ValueTask)obj);
117
118         /// <summary>Returns a value indicating whether this value is equal to a specified <see cref="ValueTask"/> value.</summary>
119         public bool Equals(ValueTask other) => _obj == other._obj && _token == other._token;
120
121         /// <summary>Returns a value indicating whether two <see cref="ValueTask"/> values are equal.</summary>
122         public static bool operator ==(ValueTask left, ValueTask right) =>
123             left.Equals(right);
124
125         /// <summary>Returns a value indicating whether two <see cref="ValueTask"/> values are not equal.</summary>
126         public static bool operator !=(ValueTask left, ValueTask right) =>
127             !left.Equals(right);
128
129         /// <summary>
130         /// Gets a <see cref="Task"/> object to represent this ValueTask.
131         /// </summary>
132         /// <remarks>
133         /// It will either return the wrapped task object if one exists, or it'll
134         /// manufacture a new task object to represent the result.
135         /// </remarks>
136         public Task AsTask()
137         {
138             object obj = _obj;
139             Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource);
140             return 
141                 obj == null ? CompletedTask :
142                 obj as Task ??
143                 GetTaskForValueTaskSource(Unsafe.As<IValueTaskSource>(obj));
144         }
145
146         /// <summary>Gets a <see cref="ValueTask"/> that may be used at any point in the future.</summary>
147         public ValueTask Preserve() => _obj == null ? this : new ValueTask(AsTask());
148
149         /// <summary>Creates a <see cref="Task"/> to represent the <see cref="IValueTaskSource"/>.</summary>
150         /// <remarks>
151         /// The <see cref="IValueTaskSource"/> is passed in rather than reading and casting <see cref="_obj"/>
152         /// so that the caller can pass in an object it's already validated.
153         /// </remarks>
154         private Task GetTaskForValueTaskSource(IValueTaskSource t)
155         {
156             ValueTaskSourceStatus status = t.GetStatus(_token);
157             if (status != ValueTaskSourceStatus.Pending)
158             {
159                 try
160                 {
161                     // Propagate any exceptions that may have occurred, then return
162                     // an already successfully completed task.
163                     t.GetResult(_token);
164                     return CompletedTask;
165
166                     // If status is Faulted or Canceled, GetResult should throw.  But
167                     // we can't guarantee every implementation will do the "right thing".
168                     // If it doesn't throw, we just treat that as success and ignore
169                     // the status.
170                 }
171                 catch (Exception exc)
172                 {
173                     if (status == ValueTaskSourceStatus.Canceled)
174                     {
175 #if !netstandard
176                         if (exc is OperationCanceledException oce)
177                         {
178                             var task = new Task<VoidTaskResult>();
179                             task.TrySetCanceled(oce.CancellationToken, oce);
180                             return task;
181                         }
182 #endif
183                         return s_canceledTask;
184                     }
185                     else
186                     {
187 #if netstandard
188                         var tcs = new TaskCompletionSource<bool>();
189                         tcs.TrySetException(exc);
190                         return tcs.Task;
191 #else
192                         return Task.FromException(exc);
193 #endif
194                     }
195                 }
196             }
197
198             var m = new ValueTaskSourceAsTask(t, _token);
199             return
200 #if netstandard
201                 m.Task;
202 #else
203                 m;
204 #endif
205         }
206
207         /// <summary>Type used to create a <see cref="Task"/> to represent a <see cref="IValueTaskSource"/>.</summary>
208         private sealed class ValueTaskSourceAsTask :
209 #if netstandard
210             TaskCompletionSource<bool>
211 #else
212             Task<VoidTaskResult>
213 #endif
214         {
215             private static readonly Action<object> s_completionAction = state =>
216             {
217                 if (!(state is ValueTaskSourceAsTask vtst) ||
218                     !(vtst._source is IValueTaskSource source))
219                 {
220                     // This could only happen if the IValueTaskSource passed the wrong state
221                     // or if this callback were invoked multiple times such that the state
222                     // was previously nulled out.
223                     ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.state);
224                     return;
225                 }
226
227                 vtst._source = null;
228                 ValueTaskSourceStatus status = source.GetStatus(vtst._token);
229                 try
230                 {
231                     source.GetResult(vtst._token);
232                     vtst.TrySetResult(default);
233                 }
234                 catch (Exception exc)
235                 {
236                     if (status == ValueTaskSourceStatus.Canceled)
237                     {
238 #if netstandard
239                         vtst.TrySetCanceled();
240 #else
241                         if (exc is OperationCanceledException oce)
242                         {
243                             vtst.TrySetCanceled(oce.CancellationToken, oce);
244                         }
245                         else
246                         {
247                             vtst.TrySetCanceled(new CancellationToken(true));
248                         }
249 #endif
250                     }
251                     else
252                     {
253                         vtst.TrySetException(exc);
254                     }
255                 }
256             };
257
258             /// <summary>The associated <see cref="IValueTaskSource"/>.</summary>
259             private IValueTaskSource _source;
260             /// <summary>The token to pass through to operations on <see cref="_source"/></summary>
261             private readonly short _token;
262
263             public ValueTaskSourceAsTask(IValueTaskSource source, short token)
264             {
265                 _token = token;
266                 _source = source;
267                 source.OnCompleted(s_completionAction, this, token, ValueTaskSourceOnCompletedFlags.None);
268             }
269         }
270
271         /// <summary>Gets whether the <see cref="ValueTask"/> represents a completed operation.</summary>
272         public bool IsCompleted
273         {
274             [MethodImpl(MethodImplOptions.AggressiveInlining)]
275             get
276             {
277                 object obj = _obj;
278                 Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource);
279
280                 if (obj == null)
281                 {
282                     return true;
283                 }
284
285                 if (obj is Task t)
286                 {
287                     return t.IsCompleted;
288                 }
289
290                 return Unsafe.As<IValueTaskSource>(obj).GetStatus(_token) != ValueTaskSourceStatus.Pending;
291             }
292         }
293
294         /// <summary>Gets whether the <see cref="ValueTask"/> represents a successfully completed operation.</summary>
295         public bool IsCompletedSuccessfully
296         {
297             [MethodImpl(MethodImplOptions.AggressiveInlining)]
298             get
299             {
300                 object obj = _obj;
301                 Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource);
302
303                 if (obj == null)
304                 {
305                     return true;
306                 }
307
308                 if (obj is Task t)
309                 {
310                     return
311 #if netstandard
312                         t.Status == TaskStatus.RanToCompletion;
313 #else
314                         t.IsCompletedSuccessfully;
315 #endif
316                 }
317
318                 return Unsafe.As<IValueTaskSource>(obj).GetStatus(_token) == ValueTaskSourceStatus.Succeeded;
319             }
320         }
321
322         /// <summary>Gets whether the <see cref="ValueTask"/> represents a failed operation.</summary>
323         public bool IsFaulted
324         {
325             get
326             {
327                 object obj = _obj;
328                 Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource);
329
330                 if (obj == null)
331                 {
332                     return false;
333                 }
334
335                 if (obj is Task t)
336                 {
337                     return t.IsFaulted;
338                 }
339
340                 return Unsafe.As<IValueTaskSource>(obj).GetStatus(_token) == ValueTaskSourceStatus.Faulted;
341             }
342         }
343
344         /// <summary>Gets whether the <see cref="ValueTask"/> represents a canceled operation.</summary>
345         /// <remarks>
346         /// If the <see cref="ValueTask"/> is backed by a result or by a <see cref="IValueTaskSource"/>,
347         /// this will always return false.  If it's backed by a <see cref="Task"/>, it'll return the
348         /// value of the task's <see cref="Task.IsCanceled"/> property.
349         /// </remarks>
350         public bool IsCanceled
351         {
352             get
353             {
354                 object obj = _obj;
355                 Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource);
356
357                 if (obj == null)
358                 {
359                     return false;
360                 }
361
362                 if (obj is Task t)
363                 {
364                     return t.IsCanceled;
365                 }
366
367                 return Unsafe.As<IValueTaskSource>(obj).GetStatus(_token) == ValueTaskSourceStatus.Canceled;
368             }
369         }
370
371         /// <summary>Throws the exception that caused the <see cref="ValueTask"/> to fail.  If it completed successfully, nothing is thrown.</summary>
372         [MethodImpl(MethodImplOptions.AggressiveInlining)]
373         [StackTraceHidden]
374         internal void ThrowIfCompletedUnsuccessfully()
375         {
376             object obj = _obj;
377             Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource);
378
379             if (obj != null)
380             {
381                 if (obj is Task t)
382                 {
383 #if netstandard
384                     t.GetAwaiter().GetResult();
385 #else
386                     TaskAwaiter.ValidateEnd(t);
387 #endif
388                 }
389                 else
390                 {
391                     Unsafe.As<IValueTaskSource>(obj).GetResult(_token);
392                 }
393             }
394         }
395
396         /// <summary>Gets an awaiter for this <see cref="ValueTask"/>.</summary>
397         public ValueTaskAwaiter GetAwaiter() => new ValueTaskAwaiter(this);
398
399         /// <summary>Configures an awaiter for this <see cref="ValueTask"/>.</summary>
400         /// <param name="continueOnCapturedContext">
401         /// true to attempt to marshal the continuation back to the captured context; otherwise, false.
402         /// </param>
403         [MethodImpl(MethodImplOptions.AggressiveInlining)]
404         public ConfiguredValueTaskAwaitable ConfigureAwait(bool continueOnCapturedContext) =>
405             new ConfiguredValueTaskAwaitable(new ValueTask(_obj, _token, continueOnCapturedContext));
406     }
407
408     /// <summary>Provides a value type that can represent a synchronously available value or a task object.</summary>
409     /// <typeparam name="TResult">Specifies the type of the result.</typeparam>
410     /// <remarks>
411     /// <see cref="ValueTask{TResult}"/>s are meant to be directly awaited.  To do more complicated operations with them, a <see cref="Task"/>
412     /// should be extracted using <see cref="AsTask"/> or <see cref="Preserve"/>.  Such operations might include caching an instance to
413     /// be awaited later, registering multiple continuations with a single operation, awaiting the same task multiple times, and using
414     /// combinators over multiple operations.
415     /// </remarks>
416     [AsyncMethodBuilder(typeof(AsyncValueTaskMethodBuilder<>))]
417     [StructLayout(LayoutKind.Auto)]
418     public readonly struct ValueTask<TResult> : IEquatable<ValueTask<TResult>>
419     {
420         /// <summary>A task canceled using `new CancellationToken(true)`. Lazily created only when first needed.</summary>
421         private static Task<TResult> s_canceledTask;
422         /// <summary>null if <see cref="_result"/> has the result, otherwise a <see cref="Task{TResult}"/> or a <see cref="IValueTaskSource{TResult}"/>.</summary>
423         internal readonly object _obj;
424         /// <summary>The result to be used if the operation completed successfully synchronously.</summary>
425         internal readonly TResult _result;
426         /// <summary>Opaque value passed through to the <see cref="IValueTaskSource{TResult}"/>.</summary>
427         internal readonly short _token;
428         /// <summary>true to continue on the captured context; otherwise, false.</summary>
429         /// <remarks>Stored in the <see cref="ValueTask{TResult}"/> rather than in the configured awaiter to utilize otherwise padding space.</remarks>
430         internal readonly bool _continueOnCapturedContext;
431
432         // An instance created with the default ctor (a zero init'd struct) represents a synchronously, successfully completed operation
433         // with a result of default(TResult).
434
435         /// <summary>Initialize the <see cref="ValueTask{TResult}"/> with a <typeparamref name="TResult"/> result value.</summary>
436         /// <param name="result">The result.</param>
437         [MethodImpl(MethodImplOptions.AggressiveInlining)]
438         public ValueTask(TResult result)
439         {
440             _result = result;
441
442             _obj = null;
443             _continueOnCapturedContext = true;
444             _token = 0;
445         }
446
447         /// <summary>Initialize the <see cref="ValueTask{TResult}"/> with a <see cref="Task{TResult}"/> that represents the operation.</summary>
448         /// <param name="task">The task.</param>
449         [MethodImpl(MethodImplOptions.AggressiveInlining)]
450         public ValueTask(Task<TResult> task)
451         {
452             if (task == null)
453             {
454                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.task);
455             }
456
457             _obj = task;
458
459             _result = default;
460             _continueOnCapturedContext = true;
461             _token = 0;
462         }
463
464         /// <summary>Initialize the <see cref="ValueTask{TResult}"/> with a <see cref="IValueTaskSource{TResult}"/> object that represents the operation.</summary>
465         /// <param name="source">The source.</param>
466         /// <param name="token">Opaque value passed through to the <see cref="IValueTaskSource"/>.</param>
467         [MethodImpl(MethodImplOptions.AggressiveInlining)]
468         public ValueTask(IValueTaskSource<TResult> source, short token)
469         {
470             if (source == null)
471             {
472                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
473             }
474
475             _obj = source;
476             _token = token;
477
478             _result = default;
479             _continueOnCapturedContext = true;
480         }
481
482         /// <summary>Non-verified initialization of the struct to the specified values.</summary>
483         /// <param name="obj">The object.</param>
484         /// <param name="result">The result.</param>
485         /// <param name="token">The token.</param>
486         /// <param name="continueOnCapturedContext">true to continue on captured context; otherwise, false.</param>
487         [MethodImpl(MethodImplOptions.AggressiveInlining)]
488         private ValueTask(object obj, TResult result, short token, bool continueOnCapturedContext)
489         {
490             _obj = obj;
491             _result = result;
492             _token = token;
493             _continueOnCapturedContext = continueOnCapturedContext;
494         }
495
496
497         /// <summary>Returns the hash code for this instance.</summary>
498         public override int GetHashCode() =>
499             _obj != null ? _obj.GetHashCode() :
500             _result != null ? _result.GetHashCode() :
501             0;
502
503         /// <summary>Returns a value indicating whether this value is equal to a specified <see cref="object"/>.</summary>
504         public override bool Equals(object obj) =>
505             obj is ValueTask<TResult> &&
506             Equals((ValueTask<TResult>)obj);
507
508         /// <summary>Returns a value indicating whether this value is equal to a specified <see cref="ValueTask{TResult}"/> value.</summary>
509         public bool Equals(ValueTask<TResult> other) =>
510             _obj != null || other._obj != null ?
511                 _obj == other._obj && _token == other._token :
512                 EqualityComparer<TResult>.Default.Equals(_result, other._result);
513
514         /// <summary>Returns a value indicating whether two <see cref="ValueTask{TResult}"/> values are equal.</summary>
515         public static bool operator ==(ValueTask<TResult> left, ValueTask<TResult> right) =>
516             left.Equals(right);
517
518         /// <summary>Returns a value indicating whether two <see cref="ValueTask{TResult}"/> values are not equal.</summary>
519         public static bool operator !=(ValueTask<TResult> left, ValueTask<TResult> right) =>
520             !left.Equals(right);
521
522         /// <summary>
523         /// Gets a <see cref="Task{TResult}"/> object to represent this ValueTask.
524         /// </summary>
525         /// <remarks>
526         /// It will either return the wrapped task object if one exists, or it'll
527         /// manufacture a new task object to represent the result.
528         /// </remarks>
529         public Task<TResult> AsTask()
530         {
531             object obj = _obj;
532             Debug.Assert(obj == null || obj is Task<TResult> || obj is IValueTaskSource<TResult>);
533
534             if (obj == null)
535             {
536                 return
537 #if netstandard
538                     Task.FromResult(_result);
539 #else
540                     AsyncTaskMethodBuilder<TResult>.GetTaskForResult(_result);
541 #endif
542             }
543
544             if (obj is Task<TResult> t)
545             {
546                 return t;
547             }
548
549             return GetTaskForValueTaskSource(Unsafe.As<IValueTaskSource<TResult>>(obj));
550         }
551
552         /// <summary>Gets a <see cref="ValueTask{TResult}"/> that may be used at any point in the future.</summary>
553         public ValueTask<TResult> Preserve() => _obj == null ? this : new ValueTask<TResult>(AsTask());
554
555         /// <summary>Creates a <see cref="Task{TResult}"/> to represent the <see cref="IValueTaskSource{TResult}"/>.</summary>
556         /// <remarks>
557         /// The <see cref="IValueTaskSource{TResult}"/> is passed in rather than reading and casting <see cref="_obj"/>
558         /// so that the caller can pass in an object it's already validated.
559         /// </remarks>
560         private Task<TResult> GetTaskForValueTaskSource(IValueTaskSource<TResult> t)
561         {
562             ValueTaskSourceStatus status = t.GetStatus(_token);
563             if (status != ValueTaskSourceStatus.Pending)
564             {
565                 try
566                 {
567                     // Get the result of the operation and return a task for it.
568                     // If any exception occurred, propagate it
569                     return
570 #if netstandard
571                         Task.FromResult(t.GetResult(_token));
572 #else
573                         AsyncTaskMethodBuilder<TResult>.GetTaskForResult(t.GetResult(_token));
574 #endif
575
576                     // If status is Faulted or Canceled, GetResult should throw.  But
577                     // we can't guarantee every implementation will do the "right thing".
578                     // If it doesn't throw, we just treat that as success and ignore
579                     // the status.
580                 }
581                 catch (Exception exc)
582                 {
583                     if (status == ValueTaskSourceStatus.Canceled)
584                     {
585 #if !netstandard
586                         if (exc is OperationCanceledException oce)
587                         {
588                             var task = new Task<TResult>();
589                             task.TrySetCanceled(oce.CancellationToken, oce);
590                             return task;
591                         }
592 #endif
593
594                         Task<TResult> canceledTask = s_canceledTask;
595                         if (canceledTask == null)
596                         {
597 #if netstandard
598                             var tcs = new TaskCompletionSource<TResult>();
599                             tcs.TrySetCanceled();
600                             canceledTask = tcs.Task;
601 #else
602                             canceledTask = Task.FromCanceled<TResult>(new CancellationToken(true));
603 #endif
604                             // Benign race condition to initialize cached task, as identity doesn't matter.
605                             s_canceledTask = canceledTask;
606                         }
607                         return canceledTask;
608                     }
609                     else
610                     {
611 #if netstandard
612                         var tcs = new TaskCompletionSource<TResult>();
613                         tcs.TrySetException(exc);
614                         return tcs.Task;
615 #else
616                         return Task.FromException<TResult>(exc);
617 #endif
618                     }
619                 }
620             }
621
622             var m = new ValueTaskSourceAsTask(t, _token);
623             return
624 #if netstandard
625                 m.Task;
626 #else
627                 m;
628 #endif
629         }
630
631         /// <summary>Type used to create a <see cref="Task{TResult}"/> to represent a <see cref="IValueTaskSource{TResult}"/>.</summary>
632         private sealed class ValueTaskSourceAsTask :
633 #if netstandard
634             TaskCompletionSource<TResult>
635 #else
636             Task<TResult>
637 #endif
638         {
639             private static readonly Action<object> s_completionAction = state =>
640             {
641                 if (!(state is ValueTaskSourceAsTask vtst) ||
642                     !(vtst._source is IValueTaskSource<TResult> source))
643                 {
644                     // This could only happen if the IValueTaskSource<TResult> passed the wrong state
645                     // or if this callback were invoked multiple times such that the state
646                     // was previously nulled out.
647                     ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.state);
648                     return;
649                 }
650
651                 vtst._source = null;
652                 ValueTaskSourceStatus status = source.GetStatus(vtst._token);
653                 try
654                 {
655                     vtst.TrySetResult(source.GetResult(vtst._token));
656                 }
657                 catch (Exception exc)
658                 {
659                     if (status == ValueTaskSourceStatus.Canceled)
660                     {
661 #if netstandard
662                         vtst.TrySetCanceled();
663 #else
664                         if (exc is OperationCanceledException oce)
665                         {
666                             vtst.TrySetCanceled(oce.CancellationToken, oce);
667                         }
668                         else
669                         {
670                             vtst.TrySetCanceled(new CancellationToken(true));
671                         }
672 #endif
673                     }
674                     else
675                     {
676                         vtst.TrySetException(exc);
677                     }
678                 }
679             };
680
681             /// <summary>The associated <see cref="IValueTaskSource"/>.</summary>
682             private IValueTaskSource<TResult> _source;
683             /// <summary>The token to pass through to operations on <see cref="_source"/></summary>
684             private readonly short _token;
685
686             public ValueTaskSourceAsTask(IValueTaskSource<TResult> source, short token)
687             {
688                 _source = source;
689                 _token = token;
690                 source.OnCompleted(s_completionAction, this, token, ValueTaskSourceOnCompletedFlags.None);
691             }
692         }
693
694         /// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a completed operation.</summary>
695         public bool IsCompleted
696         {
697             [MethodImpl(MethodImplOptions.AggressiveInlining)]
698             get
699             {
700                 object obj = _obj;
701                 Debug.Assert(obj == null || obj is Task<TResult> || obj is IValueTaskSource<TResult>);
702
703                 if (obj == null)
704                 {
705                     return true;
706                 }
707
708                 if (obj is Task<TResult> t)
709                 {
710                     return t.IsCompleted;
711                 }
712
713                 return Unsafe.As<IValueTaskSource<TResult>>(obj).GetStatus(_token) != ValueTaskSourceStatus.Pending;
714             }
715         }
716
717         /// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a successfully completed operation.</summary>
718         public bool IsCompletedSuccessfully
719         {
720             [MethodImpl(MethodImplOptions.AggressiveInlining)]
721             get
722             {
723                 object obj = _obj;
724                 Debug.Assert(obj == null || obj is Task<TResult> || obj is IValueTaskSource<TResult>);
725
726                 if (obj == null)
727                 {
728                     return true;
729                 }
730
731                 if (obj is Task<TResult> t)
732                 {
733                     return
734 #if netstandard
735                         t.Status == TaskStatus.RanToCompletion;
736 #else
737                         t.IsCompletedSuccessfully;
738 #endif
739                 }
740
741                 return Unsafe.As<IValueTaskSource<TResult>>(obj).GetStatus(_token) == ValueTaskSourceStatus.Succeeded;
742             }
743         }
744
745         /// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a failed operation.</summary>
746         public bool IsFaulted
747         {
748             get
749             {
750                 object obj = _obj;
751                 Debug.Assert(obj == null || obj is Task<TResult> || obj is IValueTaskSource<TResult>);
752
753                 if (obj == null)
754                 {
755                     return false;
756                 }
757
758                 if (obj is Task<TResult> t)
759                 {
760                     return t.IsFaulted;
761                 }
762
763                 return Unsafe.As<IValueTaskSource<TResult>>(obj).GetStatus(_token) == ValueTaskSourceStatus.Faulted;
764             }
765         }
766
767         /// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a canceled operation.</summary>
768         /// <remarks>
769         /// If the <see cref="ValueTask{TResult}"/> is backed by a result or by a <see cref="IValueTaskSource{TResult}"/>,
770         /// this will always return false.  If it's backed by a <see cref="Task"/>, it'll return the
771         /// value of the task's <see cref="Task.IsCanceled"/> property.
772         /// </remarks>
773         public bool IsCanceled
774         {
775             get
776             {
777                 object obj = _obj;
778                 Debug.Assert(obj == null || obj is Task<TResult> || obj is IValueTaskSource<TResult>);
779
780                 if (obj == null)
781                 {
782                     return false;
783                 }
784
785                 if (obj is Task<TResult> t)
786                 {
787                     return t.IsCanceled;
788                 }
789
790                 return Unsafe.As<IValueTaskSource<TResult>>(obj).GetStatus(_token) == ValueTaskSourceStatus.Canceled;
791             }
792         }
793
794         /// <summary>Gets the result.</summary>
795         public TResult Result
796         {
797             [MethodImpl(MethodImplOptions.AggressiveInlining)]
798             get
799             {
800                 object obj = _obj;
801                 Debug.Assert(obj == null || obj is Task<TResult> || obj is IValueTaskSource<TResult>);
802
803                 if (obj == null)
804                 {
805                     return _result;
806                 }
807
808                 if (obj is Task<TResult> t)
809                 {
810 #if netstandard
811                     return t.GetAwaiter().GetResult();
812 #else
813                     TaskAwaiter.ValidateEnd(t);
814                     return t.ResultOnSuccess;
815 #endif
816                 }
817
818                 return Unsafe.As<IValueTaskSource<TResult>>(obj).GetResult(_token);
819             }
820         }
821
822         /// <summary>Gets an awaiter for this <see cref="ValueTask{TResult}"/>.</summary>
823         [MethodImpl(MethodImplOptions.AggressiveInlining)]
824         public ValueTaskAwaiter<TResult> GetAwaiter() => new ValueTaskAwaiter<TResult>(this);
825
826         /// <summary>Configures an awaiter for this <see cref="ValueTask{TResult}"/>.</summary>
827         /// <param name="continueOnCapturedContext">
828         /// true to attempt to marshal the continuation back to the captured context; otherwise, false.
829         /// </param>
830         [MethodImpl(MethodImplOptions.AggressiveInlining)]
831         public ConfiguredValueTaskAwaitable<TResult> ConfigureAwait(bool continueOnCapturedContext) =>
832             new ConfiguredValueTaskAwaitable<TResult>(new ValueTask<TResult>(_obj, _result, _token, continueOnCapturedContext));
833
834         /// <summary>Gets a string-representation of this <see cref="ValueTask{TResult}"/>.</summary>
835         public override string ToString()
836         {
837             if (IsCompletedSuccessfully)
838             {
839                 TResult result = Result;
840                 if (result != null)
841                 {
842                     return result.ToString();
843                 }
844             }
845
846             return string.Empty;
847         }
848     }
849 }