Skip to content

Commit 279b749

Browse files
committed
Merge pull request #233 from sharwell/ParallelExtensions
Parallel extensions
2 parents 4a165c0 + c4cf1d6 commit 279b749

22 files changed

Lines changed: 2215 additions & 1 deletion
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<topic id="32cc6156-3b31-4450-b209-b55fcfc0a210" revisionNumber="1">
3+
<developerConceptualDocument
4+
xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5"
5+
xmlns:xlink="http://www.w3.org/1999/xlink">
6+
7+
<introduction>
8+
<para>
9+
The openstack.net SDK is migrating to an asynchronous service model for ongoing feature support.
10+
This page contains information about several aspects of the asynchronous interfaces which could
11+
result in some confusion during development. It also describes the inclusion of extension methods
12+
that allow new product features to be used in code that is not allowed to make asynchronous API
13+
calls.
14+
</para>
15+
</introduction>
16+
17+
<section address="Exceptions">
18+
<title>Exceptions Thrown by Asynchronous Methods</title>
19+
<content>
20+
<para>
21+
Asynchronous methods are capable of throwing exceptions before creating a
22+
<codeEntityReference>T:System.Threading.Tasks.Task</codeEntityReference> or during the asynchronous
23+
execution of the task itself. The asynchronous service interfaces do not distinguish between these
24+
two cases, allowing for any of the specified exceptions to be thrown in either manner.
25+
</para>
26+
<list class="bullet">
27+
<listItem>
28+
<para>
29+
Exceptions thrown prior to the creation of the
30+
<codeEntityReference>T:System.Threading.Tasks.Task</codeEntityReference> object representing the
31+
asynchronous operation must be caught directly by the calling code. For example, if the code
32+
throws an <codeEntityReference>T:System.ArgumentNullException</codeEntityReference> in this
33+
manner, the calling code would need to contain a
34+
<codeInline>catch(ArgumentNullException)</codeInline> or
35+
<codeInline>catch(ArgumentException)</codeInline> handler to handle the exception.
36+
</para>
37+
</listItem>
38+
<listItem>
39+
<para>
40+
Exceptions thrown during the asynchronous exceution of the task are wrapped in an
41+
<codeEntityReference>T:System.AggregateException</codeEntityReference> object and returned by the
42+
<codeEntityReference>P:System.Threading.Tasks.Task.Exception</codeEntityReference> property.
43+
Exceptions thrown in this manner must be handled either by a task continuation that checks the
44+
<codeEntityReference>P:System.Threading.Tasks.Task.Exception</codeEntityReference> property, or
45+
by calling <codeEntityReference autoUpgrade="true">M:System.Threading.Tasks.Task.Wait</codeEntityReference>
46+
or checking the <codeEntityReference>P:System.Threading.Tasks.Task`1.Result</codeEntityReference>
47+
property within an exception handling block that includes a
48+
<codeInline>catch(AggregateException)</codeInline> handler.
49+
</para>
50+
</listItem>
51+
</list>
52+
</content>
53+
</section>
54+
55+
<section address="SynchronousExtensions">
56+
<title>Synchronous Extensions</title>
57+
<content>
58+
<para>
59+
The namespace <codeEntityReference>N:net.openstack.Core.Synchronous</codeEntityReference> contains extension
60+
methods that allow methods in an asynchronous service interface to be invoked synchronously. These extension
61+
methods are not recommended for use in new development, but are provided as a compatibility aid for projects
62+
where external restrictions preclude the direct use of the asynchronous APIs. These extension methods perform
63+
the following functions:
64+
</para>
65+
<list class="bullet">
66+
<listItem>
67+
<para>
68+
Invoke the asynchronous method, wait for the resulting
69+
<codeEntityReference>T:System.Threading.Tasks.Task</codeEntityReference> to complete, and (where
70+
applicable) return the task result.
71+
</para>
72+
</listItem>
73+
<listItem>
74+
<para>
75+
If an exception is thrown during the asynchronous execution of the method and wrapped in an
76+
<codeEntityReference>T:System.AggregateException</codeEntityReference>, the extension method unwraps the inner exception and throws
77+
it directly, just as would occur if the underlying method were executed synchronously.
78+
</para>
79+
</listItem>
80+
</list>
81+
<para>
82+
The extensions for synchronous API calls do not expose all features of the underlying asynchronous API. In
83+
particular, the following limitations apply.
84+
</para>
85+
<list class="bullet">
86+
<listItem>
87+
<para>
88+
For asynchronous methods taking an <codeEntityReference>T:net.openstack.Core.AsyncCompletionOption</codeEntityReference> parameter to control
89+
the behavior of the task created for asynchronous server-side operations, the synchronous extension
90+
always passes <codeEntityReference>F:net.openstack.Core.AsyncCompletionOption.RequestSubmitted</codeEntityReference> for the argument.
91+
</para>
92+
</listItem>
93+
<listItem>
94+
<para>
95+
The synchronous extensions always pass <codeEntityReference>P:System.Threading.CancellationToken.None</codeEntityReference> for the
96+
<codeEntityReference>T:System.Threading.CancellationToken</codeEntityReference> argument, and do not support asynchronous cancellation of
97+
the call.
98+
</para>
99+
</listItem>
100+
<listItem>
101+
<para>
102+
The synchronous extensions do not support progress callbacks, and pass <codeInline>null</codeInline>
103+
to APIs with an <codeEntityReference>T:net.openstack.Core.IProgress`1</codeEntityReference> parameter.
104+
</para>
105+
</listItem>
106+
</list>
107+
</content>
108+
</section>
109+
110+
<relatedTopics>
111+
</relatedTopics>
112+
</developerConceptualDocument>
113+
</topic>

src/Documentation/Documentation.v3.5.shfbproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
</ProjectReference>
102102
</ItemGroup>
103103
<ItemGroup>
104+
<None Include="Content\AsynchronousServices.aml" />
104105
<None Include="Content\License.aml" />
105106
<None Include="Content\MSHelpViewerRoot.aml" />
106107
<None Include="Content\PreliminaryFeatures.aml" />

src/Documentation/Documentation.v4.0.shfbproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
</ProjectReference>
102102
</ItemGroup>
103103
<ItemGroup>
104+
<None Include="Content\AsynchronousServices.aml" />
104105
<None Include="Content\License.aml" />
105106
<None Include="Content\MSHelpViewerRoot.aml" />
106107
<None Include="Content\PreliminaryFeatures.aml" />

src/Documentation/OpenStackSDK.content

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@
1616
</HelpKeywords>
1717
</Topic>
1818
<Topic id="9c4a6074-9d84-4488-9565-50ecb5049ffe" visible="True" title="Preliminary Features" />
19+
<Topic id="32cc6156-3b31-4450-b209-b55fcfc0a210" visible="True" title="Asynchronous Services" />
1920
</Topics>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
namespace net.openstack.Core
2+
{
3+
using System.Threading.Tasks;
4+
5+
/// <summary>
6+
/// Specifies when a <see cref="Task"/> representing an asynchronous server operation
7+
/// should be considered complete.
8+
/// </summary>
9+
/// <preliminary/>
10+
public enum AsyncCompletionOption
11+
{
12+
/// <summary>
13+
/// The <see cref="Task"/> representing the operation is considered complete after the
14+
/// request has been submitted to the server.
15+
/// </summary>
16+
RequestSubmitted,
17+
18+
/// <summary>
19+
/// The <see cref="Task"/> representing the operation is considered complete after the
20+
/// server has completed processing the request.
21+
/// </summary>
22+
RequestCompleted,
23+
}
24+
}

src/corelib/Core/IProgress`1.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
namespace net.openstack.Core
2+
{
3+
/// <summary>
4+
/// Defines a provider for progress updates.
5+
/// </summary>
6+
/// <typeparam name="T">The type of progress update value.</typeparam>
7+
/// <preliminary/>
8+
public interface IProgress<
9+
#if NET35
10+
T
11+
#else
12+
in T
13+
#endif
14+
>
15+
{
16+
/// <summary>
17+
/// Reports a progress update.
18+
/// </summary>
19+
/// <param name="value">The value of the updated progress.</param>
20+
void Report(T value);
21+
}
22+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
namespace net.openstack.Core
2+
{
3+
using System.Threading.Tasks;
4+
5+
/// <summary>
6+
/// Provides extension methods to <see cref="Task"/> and <see cref="Task{TResult}"/> instances
7+
/// for use within the openstack.net library.
8+
/// </summary>
9+
/// <threadsafety static="true" instance="false"/>
10+
/// <preliminary/>
11+
internal static class InternalTaskExtensions
12+
{
13+
/// <summary>
14+
/// Gets a completed <see cref="Task"/>.
15+
/// </summary>
16+
/// <returns>A completed <see cref="Task"/>.</returns>
17+
public static Task CompletedTask()
18+
{
19+
return CompletedTaskHolder.Default;
20+
}
21+
22+
/// <summary>
23+
/// Gets a completed <see cref="Task{TResult}"/> with the specified result.
24+
/// </summary>
25+
/// <typeparam name="TResult">The task result type.</typeparam>
26+
/// <param name="result">The result of the completed task.</param>
27+
/// <returns>A completed <see cref="Task{TResult}"/>, whose <see cref="Task{TResult}.Result"/> property returns the specified <paramref name="result"/>.</returns>
28+
public static Task<TResult> CompletedTask<TResult>(TResult result)
29+
{
30+
TaskCompletionSource<TResult> completionSource = new TaskCompletionSource<TResult>();
31+
completionSource.SetResult(result);
32+
return completionSource.Task;
33+
}
34+
35+
private static class CompletedTaskHolder
36+
{
37+
public static readonly Task Default;
38+
39+
static CompletedTaskHolder()
40+
{
41+
Default = CompletedTaskHolder<object>.Default;
42+
}
43+
}
44+
45+
private static class CompletedTaskHolder<T>
46+
{
47+
public static readonly Task<T> Default;
48+
49+
static CompletedTaskHolder()
50+
{
51+
TaskCompletionSource<T> completionSource = new TaskCompletionSource<T>();
52+
completionSource.SetResult(default(T));
53+
Default = completionSource.Task;
54+
}
55+
}
56+
}
57+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//--------------------------------------------------------------------------
2+
//
3+
// Copyright (c) Microsoft Corporation. All rights reserved.
4+
//
5+
// File: TaskCompletionSourceExtensions.cs
6+
//
7+
//--------------------------------------------------------------------------
8+
9+
namespace System.Threading.Tasks
10+
{
11+
/// <summary>Extension methods for TaskCompletionSource.</summary>
12+
/// <preliminary/>
13+
internal static class TaskCompletionSourceExtensions
14+
{
15+
/// <summary>Transfers the result of a Task to the TaskCompletionSource.</summary>
16+
/// <typeparam name="TResult">Specifies the type of the result.</typeparam>
17+
/// <param name="resultSetter">The TaskCompletionSource.</param>
18+
/// <param name="task">The task whose completion results should be transferred.</param>
19+
public static void SetFromTask<TResult>(this TaskCompletionSource<TResult> resultSetter, Task task)
20+
{
21+
switch (task.Status)
22+
{
23+
case TaskStatus.RanToCompletion: resultSetter.SetResult(task is Task<TResult> ? ((Task<TResult>)task).Result : default(TResult)); break;
24+
case TaskStatus.Faulted: resultSetter.SetException(task.Exception.InnerExceptions); break;
25+
case TaskStatus.Canceled: resultSetter.SetCanceled(); break;
26+
default: throw new InvalidOperationException("The task was not completed.");
27+
}
28+
}
29+
30+
/// <summary>Transfers the result of a Task to the TaskCompletionSource.</summary>
31+
/// <typeparam name="TResult">Specifies the type of the result.</typeparam>
32+
/// <param name="resultSetter">The TaskCompletionSource.</param>
33+
/// <param name="task">The task whose completion results should be transferred.</param>
34+
public static void SetFromTask<TResult>(this TaskCompletionSource<TResult> resultSetter, Task<TResult> task)
35+
{
36+
SetFromTask(resultSetter, (Task)task);
37+
}
38+
39+
/// <summary>Attempts to transfer the result of a Task to the TaskCompletionSource.</summary>
40+
/// <typeparam name="TResult">Specifies the type of the result.</typeparam>
41+
/// <param name="resultSetter">The TaskCompletionSource.</param>
42+
/// <param name="task">The task whose completion results should be transferred.</param>
43+
/// <returns>Whether the transfer could be completed.</returns>
44+
public static bool TrySetFromTask<TResult>(this TaskCompletionSource<TResult> resultSetter, Task task)
45+
{
46+
switch (task.Status)
47+
{
48+
case TaskStatus.RanToCompletion: return resultSetter.TrySetResult(task is Task<TResult> ? ((Task<TResult>)task).Result : default(TResult));
49+
case TaskStatus.Faulted: return resultSetter.TrySetException(task.Exception.InnerExceptions);
50+
case TaskStatus.Canceled: return resultSetter.TrySetCanceled();
51+
default: throw new InvalidOperationException("The task was not completed.");
52+
}
53+
}
54+
55+
/// <summary>Attempts to transfer the result of a Task to the TaskCompletionSource.</summary>
56+
/// <typeparam name="TResult">Specifies the type of the result.</typeparam>
57+
/// <param name="resultSetter">The TaskCompletionSource.</param>
58+
/// <param name="task">The task whose completion results should be transferred.</param>
59+
/// <returns>Whether the transfer could be completed.</returns>
60+
public static bool TrySetFromTask<TResult>(this TaskCompletionSource<TResult> resultSetter, Task<TResult> task)
61+
{
62+
return TrySetFromTask(resultSetter, (Task)task);
63+
}
64+
}
65+
}

0 commit comments

Comments
 (0)