Skip to content

Commit f52c70d

Browse files
authored
Add non generic TaskCompletionSource (#203)
1 parent 4c59ac8 commit f52c70d

File tree

6 files changed

+259
-1
lines changed

6 files changed

+259
-1
lines changed

apiCount.include.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
**API count: 327**
1+
**API count: 328**

api_list.include.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,3 +554,4 @@
554554
* `void NotWhitespace(Span<Char>)`
555555

556556

557+
#### TaskCompletionSource

src/Consume/Consume.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,11 @@ void XDocumentSaveAsync()
476476
document.SaveAsync(new MemoryStream(), SaveOptions.None, CancellationToken.None);
477477
}
478478

479+
void NonGenericTaskCompletionSource()
480+
{
481+
var tcs = new TaskCompletionSource();
482+
}
483+
479484
#if FeatureMemory
480485
void RandomNextBytesSpan()
481486
{
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
// <auto-generated />
2+
#pragma warning disable
3+
4+
#if !NET5_0_OR_GREATER
5+
6+
#nullable enable
7+
8+
namespace System.Diagnostics;
9+
10+
using System;
11+
using System.Threading;
12+
using System.Threading.Tasks;
13+
using System.Collections.Generic;
14+
using System.Diagnostics.CodeAnalysis;
15+
using Link = System.ComponentModel.DescriptionAttribute;
16+
17+
/// <summary>
18+
/// Represents the producer side of a <see cref="Tasks.Task"/> unbound to a
19+
/// delegate, providing access to the consumer side through the <see cref="Tasks.Task"/> property.
20+
/// </summary>
21+
/// <remarks>
22+
/// <para>
23+
/// It is often the case that a <see cref="Tasks.Task"/> is desired to
24+
/// represent another asynchronous operation.
25+
/// <see cref="TaskCompletionSource">TaskCompletionSource</see> is provided for this purpose. It enables
26+
/// the creation of a task that can be handed out to consumers, and those consumers can use the members
27+
/// of the task as they would any other. However, unlike most tasks, the state of a task created by a
28+
/// TaskCompletionSource is controlled explicitly by the methods on TaskCompletionSource. This enables the
29+
/// completion of the external asynchronous operation to be propagated to the underlying Task. The
30+
/// separation also ensures that consumers are not able to transition the state without access to the
31+
/// corresponding TaskCompletionSource.
32+
/// </para>
33+
/// <para>
34+
/// All members of <see cref="TaskCompletionSource"/> are thread-safe
35+
/// and may be used from multiple threads concurrently.
36+
/// </para>
37+
/// </remarks>
38+
[ExcludeFromCodeCoverage]
39+
[DebuggerNonUserCode]
40+
[Link("https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcompletionsource?view=net-8.0")]
41+
#if PolyPublic
42+
public
43+
#endif
44+
class TaskCompletionSource
45+
{
46+
TaskCompletionSource<object?> inner;
47+
48+
/// <summary>Creates a <see cref="TaskCompletionSource"/>.</summary>
49+
public TaskCompletionSource() =>
50+
inner = new TaskCompletionSource<object>();
51+
52+
/// <summary>Creates a <see cref="TaskCompletionSource"/> with the specified options.</summary>
53+
/// <remarks>
54+
/// The <see cref="Tasks.Task"/> created by this instance and accessible through its <see cref="Task"/> property
55+
/// will be instantiated using the specified <paramref name="creationOptions"/>.
56+
/// </remarks>
57+
/// <param name="creationOptions">The options to use when creating the underlying <see cref="Tasks.Task"/>.</param>
58+
/// <exception cref="ArgumentOutOfRangeException">
59+
/// The <paramref name="creationOptions"/> represent options invalid for use
60+
/// with a <see cref="TaskCompletionSource"/>.
61+
/// </exception>
62+
public TaskCompletionSource(TaskCreationOptions creationOptions) :
63+
this(null, creationOptions)
64+
{
65+
}
66+
67+
/// <summary>Creates a <see cref="TaskCompletionSource"/> with the specified state.</summary>
68+
/// <param name="state">The state to use as the underlying
69+
/// <see cref="Tasks.Task"/>'s AsyncState.</param>
70+
public TaskCompletionSource(object? state) :
71+
this(state, TaskCreationOptions.None)
72+
{
73+
}
74+
75+
/// <summary>Creates a <see cref="TaskCompletionSource"/> with the specified state and options.</summary>
76+
/// <param name="creationOptions">The options to use when creating the underlying <see cref="Tasks.Task"/>.</param>
77+
/// <param name="state">The state to use as the underlying <see cref="Tasks.Task"/>'s AsyncState.</param>
78+
/// <exception cref="ArgumentOutOfRangeException">The <paramref name="creationOptions"/> represent options invalid for use with a <see cref="TaskCompletionSource"/>.</exception>
79+
public TaskCompletionSource(object? state, TaskCreationOptions creationOptions) =>
80+
inner = new(state, creationOptions);
81+
82+
/// <summary>
83+
/// Gets the <see cref="Tasks.Task"/> created
84+
/// by this <see cref="TaskCompletionSource"/>.
85+
/// </summary>
86+
/// <remarks>
87+
/// This property enables a consumer access to the <see cref="Task"/> that is controlled by this instance.
88+
/// The <see cref="SetResult"/>, <see cref="SetException(Exception)"/>, <see cref="SetException(IEnumerable{Exception})"/>,
89+
/// and <see cref="SetCanceled"/> methods (and their "Try" variants) on this instance all result in the relevant state
90+
/// transitions on this underlying Task.
91+
/// </remarks>
92+
public Task Task => inner.Task;
93+
94+
/// <summary>Transitions the underlying <see cref="Tasks.Task"/> into the <see cref="TaskStatus.Faulted"/> state.</summary>
95+
/// <param name="exception">The exception to bind to this <see cref="Tasks.Task"/>.</param>
96+
/// <exception cref="ArgumentNullException">The <paramref name="exception"/> argument is null.</exception>
97+
/// <exception cref="InvalidOperationException">
98+
/// The underlying <see cref="Tasks.Task"/> is already in one of the three final states:
99+
/// <see cref="TaskStatus.RanToCompletion"/>,
100+
/// <see cref="TaskStatus.Faulted"/>, or
101+
/// <see cref="TaskStatus.Canceled"/>.
102+
/// </exception>
103+
public void SetException(Exception exception) =>
104+
inner.SetException(exception);
105+
106+
/// <summary>Transitions the underlying <see cref="Tasks.Task"/> into the <see cref="TaskStatus.Faulted"/> state.</summary>
107+
/// <param name="exceptions">The collection of exceptions to bind to this <see cref="Tasks.Task"/>.</param>
108+
/// <exception cref="ArgumentNullException">The <paramref name="exceptions"/> argument is null.</exception>
109+
/// <exception cref="ArgumentException">There are one or more null elements in <paramref name="exceptions"/>.</exception>
110+
/// <exception cref="InvalidOperationException">
111+
/// The underlying <see cref="Tasks.Task"/> is already in one of the three final states:
112+
/// <see cref="TaskStatus.RanToCompletion"/>,
113+
/// <see cref="TaskStatus.Faulted"/>, or
114+
/// <see cref="TaskStatus.Canceled"/>.
115+
/// </exception>
116+
public void SetException(IEnumerable<Exception> exceptions) =>
117+
inner.SetException(exceptions);
118+
119+
/// <summary>
120+
/// Attempts to transition the underlying <see cref="Tasks.Task"/> into the <see cref="TaskStatus.Faulted"/> state.
121+
/// </summary>
122+
/// <param name="exception">The exception to bind to this <see cref="Tasks.Task"/>.</param>
123+
/// <returns>True if the operation was successful; otherwise, false.</returns>
124+
/// <remarks>
125+
/// This operation will return false if the <see cref="Tasks.Task"/> is already in one of the three final states:
126+
/// <see cref="TaskStatus.RanToCompletion"/>,
127+
/// <see cref="TaskStatus.Faulted"/>, or
128+
/// <see cref="TaskStatus.Canceled"/>.
129+
/// </remarks>
130+
/// <exception cref="ArgumentNullException">The <paramref name="exception"/> argument is null.</exception>
131+
public bool TrySetException(Exception exception) =>
132+
inner.TrySetException(exception);
133+
134+
/// <summary>
135+
/// Attempts to transition the underlying <see cref="Tasks.Task"/> into the <see cref="TaskStatus.Faulted"/> state.
136+
/// </summary>
137+
/// <param name="exceptions">The collection of exceptions to bind to this <see cref="Tasks.Task"/>.</param>
138+
/// <returns>True if the operation was successful; otherwise, false.</returns>
139+
/// <remarks>
140+
/// This operation will return false if the <see cref="Tasks.Task"/> is already in one of the three final states:
141+
/// <see cref="TaskStatus.RanToCompletion"/>,
142+
/// <see cref="TaskStatus.Faulted"/>, or
143+
/// <see cref="TaskStatus.Canceled"/>.
144+
/// </remarks>
145+
/// <exception cref="ArgumentNullException">The <paramref name="exceptions"/> argument is null.</exception>
146+
/// <exception cref="ArgumentException">There are one or more null elements in <paramref name="exceptions"/>.</exception>
147+
/// <exception cref="ArgumentException">The <paramref name="exceptions"/> collection is empty.</exception>
148+
public bool TrySetException(IEnumerable<Exception> exceptions) =>
149+
inner.TrySetException(exceptions);
150+
151+
/// <summary>
152+
/// Transitions the underlying <see cref="Tasks.Task"/> into the <see cref="TaskStatus.RanToCompletion"/> state.
153+
/// </summary>
154+
/// <exception cref="InvalidOperationException">
155+
/// The underlying <see cref="Tasks.Task"/> is already in one of the three final states:
156+
/// <see cref="TaskStatus.RanToCompletion"/>,
157+
/// <see cref="TaskStatus.Faulted"/>, or
158+
/// <see cref="TaskStatus.Canceled"/>.
159+
/// </exception>
160+
public void SetResult() =>
161+
inner.SetResult(null);
162+
163+
/// <summary>
164+
/// Attempts to transition the underlying <see cref="Tasks.Task"/> into the <see cref="TaskStatus.RanToCompletion"/> state.
165+
/// </summary>
166+
/// <returns>True if the operation was successful; otherwise, false.</returns>
167+
/// <remarks>
168+
/// This operation will return false if the <see cref="Tasks.Task"/> is already in one of the three final states:
169+
/// <see cref="TaskStatus.RanToCompletion"/>,
170+
/// <see cref="TaskStatus.Faulted"/>, or
171+
/// <see cref="TaskStatus.Canceled"/>.
172+
/// </remarks>
173+
public bool TrySetResult() =>
174+
inner.TrySetResult(null);
175+
176+
/// <summary>
177+
/// Transitions the underlying <see cref="Tasks.Task"/> into the <see cref="TaskStatus.Canceled"/> state.
178+
/// </summary>
179+
/// <exception cref="InvalidOperationException">
180+
/// The underlying <see cref="Tasks.Task"/> is already in one of the three final states:
181+
/// <see cref="TaskStatus.RanToCompletion"/>,
182+
/// <see cref="TaskStatus.Faulted"/>, or
183+
/// <see cref="TaskStatus.Canceled"/>.
184+
/// </exception>
185+
public void SetCanceled() => SetCanceled(default);
186+
187+
/// <summary>
188+
/// Transitions the underlying <see cref="Tasks.Task"/> into the <see cref="TaskStatus.Canceled"/> state
189+
/// using the specified token.
190+
/// </summary>
191+
/// <param name="cancellationToken">The cancellation token with which to cancel the <see cref="Tasks.Task"/>.</param>
192+
/// <exception cref="InvalidOperationException">
193+
/// The underlying <see cref="Tasks.Task"/> is already in one of the three final states:
194+
/// <see cref="TaskStatus.RanToCompletion"/>,
195+
/// <see cref="TaskStatus.Faulted"/>, or
196+
/// <see cref="TaskStatus.Canceled"/>.
197+
/// </exception>
198+
public void SetCanceled(CancellationToken cancellationToken) =>
199+
inner.SetCanceled(cancellationToken);
200+
201+
/// <summary>
202+
/// Attempts to transition the underlying <see cref="Tasks.Task"/> into the <see cref="TaskStatus.Canceled"/> state.
203+
/// </summary>
204+
/// <returns>True if the operation was successful; otherwise, false.</returns>
205+
/// <remarks>
206+
/// This operation will return false if the <see cref="Tasks.Task"/> is already in one of the three final states:
207+
/// <see cref="TaskStatus.RanToCompletion"/>,
208+
/// <see cref="TaskStatus.Faulted"/>, or
209+
/// <see cref="TaskStatus.Canceled"/>.
210+
/// </remarks>
211+
public bool TrySetCanceled() =>
212+
TrySetCanceled(default);
213+
214+
/// <summary>
215+
/// Attempts to transition the underlying <see cref="Tasks.Task"/> into the <see cref="TaskStatus.Canceled"/> state.
216+
/// </summary>
217+
/// <param name="cancellationToken">The cancellation token with which to cancel the <see cref="Tasks.Task"/>.</param>
218+
/// <returns>True if the operation was successful; otherwise, false.</returns>
219+
/// <remarks>
220+
/// This operation will return false if the <see cref="Tasks.Task"/> is already in one of the three final states:
221+
/// <see cref="TaskStatus.RanToCompletion"/>,
222+
/// <see cref="TaskStatus.Faulted"/>, or
223+
/// <see cref="TaskStatus.Canceled"/>.
224+
/// </remarks>
225+
public bool TrySetCanceled(CancellationToken cancellationToken) =>
226+
inner.TrySetCanceled(default);
227+
}
228+
#endif

src/Tests/BuildApiTest.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ public void Run()
6565
WriteHelper(types, "ULongPolyfill", writer, ref count);
6666
WriteHelper(types, "UShortPolyfill", writer, ref count);
6767
WriteHelper(types, "Guard", writer, ref count);
68+
WriteType(nameof(TaskCompletionSource), writer, ref count);
6869

6970
count += types.Count(_ => _.Key.EndsWith("Attribute"));
7071
var countMd = Path.Combine(solutionDirectory, "..", "apiCount.include.md");
@@ -136,6 +137,12 @@ static void WriteHelper(Dictionary<string, List<MethodDefinition>> types, string
136137
writer.WriteLine();
137138
}
138139

140+
static void WriteType(string name, StreamWriter writer, ref int count)
141+
{
142+
writer.WriteLine($"#### {name}");
143+
count++;
144+
}
145+
139146
static string GetTypeName(string targetType)
140147
{
141148
var name = targetType.Replace("`1", "").Replace("`2", "").Replace("`3", "");

src/Tests/PolyfillTests_TaskCompletionSource.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,23 @@
11
#pragma warning disable CS4014
22
partial class PolyfillTests
33
{
4+
[Test]
5+
public async Task TaskCompletionSource()
6+
{
7+
var completionSource = new TaskCompletionSource();
8+
9+
// Simulate some background work
10+
Task.Run(async () =>
11+
{
12+
await Task.Delay(10); // Simulate a delay
13+
completionSource.SetResult();
14+
});
15+
16+
// Await the task
17+
await completionSource.Task;
18+
Console.WriteLine("Task completed successfully");
19+
}
20+
421
[Test]
522
public async Task TaskCompletionSource_SetCanceled_WithCancellationToken()
623
{

0 commit comments

Comments
 (0)