Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit af15a20

Browse files
jkotassafern
authored andcommitted
Move TaskToApm to shared CoreLib partition (dotnet/coreclr#15113)
- Get TaskToApm in sync with CoreFX copy and move it to shared CoreLib partition - Delete redundant __Error file - Delete remaining uses of InternalBlockCopy and replace it with BlockCopy Signed-off-by: dotnet-bot-corefx-mirror <dotnet-bot@microsoft.com>
1 parent 700ac4e commit af15a20

File tree

2 files changed

+190
-0
lines changed

2 files changed

+190
-0
lines changed

src/Common/src/CoreLib/System.Private.CoreLib.Shared.projitems

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,7 @@
510510
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\SynchronizationLockException.cs" />
511511
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\TaskCanceledException.cs" />
512512
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\TaskExtensions.cs" />
513+
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\TaskToApm.cs" />
513514
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\TaskSchedulerException.cs" />
514515
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\ValueTask.cs" />
515516
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadAbortException.cs" />
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
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+
// Helper methods for using Tasks to implement the APM pattern.
6+
//
7+
// Example usage, wrapping a Task<int>-returning FooAsync method with Begin/EndFoo methods:
8+
//
9+
// public IAsyncResult BeginFoo(..., AsyncCallback callback, object state)
10+
// {
11+
// Task<int> t = FooAsync(...);
12+
// return TaskToApm.Begin(t, callback, state);
13+
// }
14+
// public int EndFoo(IAsyncResult asyncResult)
15+
// {
16+
// return TaskToApm.End<int>(asyncResult);
17+
// }
18+
19+
using System.Diagnostics;
20+
21+
namespace System.Threading.Tasks
22+
{
23+
/// <summary>
24+
/// Provides support for efficiently using Tasks to implement the APM (Begin/End) pattern.
25+
/// </summary>
26+
internal static class TaskToApm
27+
{
28+
/// <summary>
29+
/// Marshals the Task as an IAsyncResult, using the supplied callback and state
30+
/// to implement the APM pattern.
31+
/// </summary>
32+
/// <param name="task">The Task to be marshaled.</param>
33+
/// <param name="callback">The callback to be invoked upon completion.</param>
34+
/// <param name="state">The state to be stored in the IAsyncResult.</param>
35+
/// <returns>An IAsyncResult to represent the task's asynchronous operation.</returns>
36+
public static IAsyncResult Begin(Task task, AsyncCallback callback, object state)
37+
{
38+
Debug.Assert(task != null);
39+
40+
// If the task has already completed, then since the Task's CompletedSynchronously==false
41+
// and we want it to be true, we need to create a new IAsyncResult. (We also need the AsyncState to match.)
42+
IAsyncResult asyncResult;
43+
if (task.IsCompleted)
44+
{
45+
// Synchronous completion.
46+
asyncResult = new TaskWrapperAsyncResult(task, state, completedSynchronously: true);
47+
callback?.Invoke(asyncResult);
48+
}
49+
else
50+
{
51+
// For asynchronous completion we need to schedule a callback. Whether we can use the Task as the IAsyncResult
52+
// depends on whether the Task's AsyncState has reference equality with the requested state.
53+
asyncResult = task.AsyncState == state ? (IAsyncResult)task : new TaskWrapperAsyncResult(task, state, completedSynchronously: false);
54+
if (callback != null)
55+
{
56+
InvokeCallbackWhenTaskCompletes(task, callback, asyncResult);
57+
}
58+
}
59+
return asyncResult;
60+
}
61+
62+
/// <summary>Processes an IAsyncResult returned by Begin.</summary>
63+
/// <param name="asyncResult">The IAsyncResult to unwrap.</param>
64+
public static void End(IAsyncResult asyncResult)
65+
{
66+
Task task;
67+
68+
// If the IAsyncResult is our task-wrapping IAsyncResult, extract the Task.
69+
var twar = asyncResult as TaskWrapperAsyncResult;
70+
if (twar != null)
71+
{
72+
task = twar.Task;
73+
Debug.Assert(task != null, "TaskWrapperAsyncResult should never wrap a null Task.");
74+
}
75+
else
76+
{
77+
// Otherwise, the IAsyncResult should be a Task.
78+
task = asyncResult as Task;
79+
}
80+
81+
// Make sure we actually got a task, then complete the operation by waiting on it.
82+
if (task == null)
83+
{
84+
throw new ArgumentNullException();
85+
}
86+
87+
task.GetAwaiter().GetResult();
88+
}
89+
90+
/// <summary>Processes an IAsyncResult returned by Begin.</summary>
91+
/// <param name="asyncResult">The IAsyncResult to unwrap.</param>
92+
public static TResult End<TResult>(IAsyncResult asyncResult)
93+
{
94+
Task<TResult> task;
95+
96+
// If the IAsyncResult is our task-wrapping IAsyncResult, extract the Task.
97+
var twar = asyncResult as TaskWrapperAsyncResult;
98+
if (twar != null)
99+
{
100+
task = twar.Task as Task<TResult>;
101+
Debug.Assert(twar.Task != null, "TaskWrapperAsyncResult should never wrap a null Task.");
102+
}
103+
else
104+
{
105+
// Otherwise, the IAsyncResult should be a Task<TResult>.
106+
task = asyncResult as Task<TResult>;
107+
}
108+
109+
// Make sure we actually got a task, then complete the operation by waiting on it.
110+
if (task == null)
111+
{
112+
throw new ArgumentNullException();
113+
}
114+
115+
return task.GetAwaiter().GetResult();
116+
}
117+
118+
/// <summary>Invokes the callback asynchronously when the task has completed.</summary>
119+
/// <param name="antecedent">The Task to await.</param>
120+
/// <param name="callback">The callback to invoke when the Task completes.</param>
121+
/// <param name="asyncResult">The Task used as the IAsyncResult.</param>
122+
private static void InvokeCallbackWhenTaskCompletes(Task antecedent, AsyncCallback callback, IAsyncResult asyncResult)
123+
{
124+
Debug.Assert(antecedent != null);
125+
Debug.Assert(callback != null);
126+
Debug.Assert(asyncResult != null);
127+
128+
// We use OnCompleted rather than ContinueWith in order to avoid running synchronously
129+
// if the task has already completed by the time we get here. This is separated out into
130+
// its own method currently so that we only pay for the closure if necessary.
131+
antecedent.ConfigureAwait(continueOnCapturedContext: false)
132+
.GetAwaiter()
133+
.OnCompleted(() => callback(asyncResult));
134+
135+
// PERFORMANCE NOTE:
136+
// Assuming we're in the default ExecutionContext, the "slow path" of an incomplete
137+
// task will result in four allocations: the new IAsyncResult, the delegate+closure
138+
// in this method, and the continuation object inside of OnCompleted (necessary
139+
// to capture both the Action delegate and the ExecutionContext in a single object).
140+
// In the future, if performance requirements drove a need, those four
141+
// allocations could be reduced to one. This would be achieved by having TaskWrapperAsyncResult
142+
// also implement ITaskCompletionAction (and optionally IThreadPoolWorkItem). It would need
143+
// additional fields to store the AsyncCallback and an ExecutionContext. Once configured,
144+
// it would be set into the Task as a continuation. Its Invoke method would then be run when
145+
// the antecedent completed, and, doing all of the necessary work to flow ExecutionContext,
146+
// it would invoke the AsyncCallback. It could also have a field on it for the antecedent,
147+
// so that the End method would have access to the completed antecedent. For related examples,
148+
// see other implementations of ITaskCompletionAction, and in particular ReadWriteTask
149+
// used in Stream.Begin/EndXx's implementation.
150+
}
151+
152+
/// <summary>
153+
/// Provides a simple IAsyncResult that wraps a Task. This, in effect, allows
154+
/// for overriding what's seen for the CompletedSynchronously and AsyncState values.
155+
/// </summary>
156+
private sealed class TaskWrapperAsyncResult : IAsyncResult
157+
{
158+
/// <summary>The wrapped Task.</summary>
159+
internal readonly Task Task;
160+
/// <summary>The new AsyncState value.</summary>
161+
private readonly object _state;
162+
/// <summary>The new CompletedSynchronously value.</summary>
163+
private readonly bool _completedSynchronously;
164+
165+
/// <summary>Initializes the IAsyncResult with the Task to wrap and the overriding AsyncState and CompletedSynchronously values.</summary>
166+
/// <param name="task">The Task to wrap.</param>
167+
/// <param name="state">The new AsyncState value</param>
168+
/// <param name="completedSynchronously">The new CompletedSynchronously value.</param>
169+
internal TaskWrapperAsyncResult(Task task, object state, bool completedSynchronously)
170+
{
171+
Debug.Assert(task != null);
172+
Debug.Assert(!completedSynchronously || task.IsCompleted, "If completedSynchronously is true, the task must be completed.");
173+
174+
this.Task = task;
175+
_state = state;
176+
_completedSynchronously = completedSynchronously;
177+
}
178+
179+
// The IAsyncResult implementation.
180+
// - IsCompleted and AsyncWaitHandle just pass through to the Task.
181+
// - AsyncState and CompletedSynchronously return the corresponding values stored in this object.
182+
183+
object IAsyncResult.AsyncState { get { return _state; } }
184+
bool IAsyncResult.CompletedSynchronously { get { return _completedSynchronously; } }
185+
bool IAsyncResult.IsCompleted { get { return this.Task.IsCompleted; } }
186+
WaitHandle IAsyncResult.AsyncWaitHandle { get { return ((IAsyncResult)this.Task).AsyncWaitHandle; } }
187+
}
188+
}
189+
}

0 commit comments

Comments
 (0)