Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions src/Documentation/Content/AsynchronousServices.aml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?xml version="1.0" encoding="utf-8"?>
<topic id="32cc6156-3b31-4450-b209-b55fcfc0a210" revisionNumber="1">
<developerConceptualDocument
xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5"
xmlns:xlink="http://www.w3.org/1999/xlink">

<introduction>
<para>
The openstack.net SDK is migrating to an asynchronous service model for ongoing feature support.
This page contains information about several aspects of the asynchronous interfaces which could
result in some confusion during development. It also describes the inclusion of extension methods
that allow new product features to be used in code that is not allowed to make asynchronous API
calls.
</para>
</introduction>

<section address="Exceptions">
<title>Exceptions Thrown by Asynchronous Methods</title>
<content>
<para>
Asynchronous methods are capable of throwing exceptions before creating a
<codeEntityReference>T:System.Threading.Tasks.Task</codeEntityReference> or during the asynchronous
execution of the task itself. The asynchronous service interfaces do not distinguish between these
two cases, allowing for any of the specified exceptions to be thrown in either manner.
</para>
<list class="bullet">
<listItem>
<para>
Exceptions thrown prior to the creation of the
<codeEntityReference>T:System.Threading.Tasks.Task</codeEntityReference> object representing the
asynchronous operation must be caught directly by the calling code. For example, if the code
throws an <codeEntityReference>T:System.ArgumentNullException</codeEntityReference> in this
manner, the calling code would need to contain a
<codeInline>catch(ArgumentNullException)</codeInline> or
<codeInline>catch(ArgumentException)</codeInline> handler to handle the exception.
</para>
</listItem>
<listItem>
<para>
Exceptions thrown during the asynchronous exceution of the task are wrapped in an
<codeEntityReference>T:System.AggregateException</codeEntityReference> object and returned by the
<codeEntityReference>P:System.Threading.Tasks.Task.Exception</codeEntityReference> property.
Exceptions thrown in this manner must be handled either by a task continuation that checks the
<codeEntityReference>P:System.Threading.Tasks.Task.Exception</codeEntityReference> property, or
by calling <codeEntityReference autoUpgrade="true">M:System.Threading.Tasks.Task.Wait</codeEntityReference>
or checking the <codeEntityReference>P:System.Threading.Tasks.Task`1.Result</codeEntityReference>
property within an exception handling block that includes a
<codeInline>catch(AggregateException)</codeInline> handler.
</para>
</listItem>
</list>
</content>
</section>

<section address="SynchronousExtensions">
<title>Synchronous Extensions</title>
<content>
<para>
The namespace <codeEntityReference>N:net.openstack.Core.Synchronous</codeEntityReference> contains extension
methods that allow methods in an asynchronous service interface to be invoked synchronously. These extension
methods are not recommended for use in new development, but are provided as a compatibility aid for projects
where external restrictions preclude the direct use of the asynchronous APIs. These extension methods perform
the following functions:
</para>
<list class="bullet">
<listItem>
<para>
Invoke the asynchronous method, wait for the resulting
<codeEntityReference>T:System.Threading.Tasks.Task</codeEntityReference> to complete, and (where
applicable) return the task result.
</para>
</listItem>
<listItem>
<para>
If an exception is thrown during the asynchronous execution of the method and wrapped in an
<codeEntityReference>T:System.AggregateException</codeEntityReference>, the extension method unwraps the inner exception and throws
it directly, just as would occur if the underlying method were executed synchronously.
</para>
</listItem>
</list>
<para>
The extensions for synchronous API calls do not expose all features of the underlying asynchronous API. In
particular, the following limitations apply.
</para>
<list class="bullet">
<listItem>
<para>
For asynchronous methods taking an <codeEntityReference>T:net.openstack.Core.AsyncCompletionOption</codeEntityReference> parameter to control
the behavior of the task created for asynchronous server-side operations, the synchronous extension
always passes <codeEntityReference>F:net.openstack.Core.AsyncCompletionOption.RequestSubmitted</codeEntityReference> for the argument.
</para>
</listItem>
<listItem>
<para>
The synchronous extensions always pass <codeEntityReference>P:System.Threading.CancellationToken.None</codeEntityReference> for the
<codeEntityReference>T:System.Threading.CancellationToken</codeEntityReference> argument, and do not support asynchronous cancellation of
the call.
</para>
</listItem>
<listItem>
<para>
The synchronous extensions do not support progress callbacks, and pass <codeInline>null</codeInline>
to APIs with an <codeEntityReference>T:net.openstack.Core.IProgress`1</codeEntityReference> parameter.
</para>
</listItem>
</list>
</content>
</section>

<relatedTopics>
</relatedTopics>
</developerConceptualDocument>
</topic>
1 change: 1 addition & 0 deletions src/Documentation/Documentation.v3.5.shfbproj
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="Content\AsynchronousServices.aml" />
<None Include="Content\License.aml" />
<None Include="Content\MSHelpViewerRoot.aml" />
<None Include="Content\PreliminaryFeatures.aml" />
Expand Down
1 change: 1 addition & 0 deletions src/Documentation/Documentation.v4.0.shfbproj
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="Content\AsynchronousServices.aml" />
<None Include="Content\License.aml" />
<None Include="Content\MSHelpViewerRoot.aml" />
<None Include="Content\PreliminaryFeatures.aml" />
Expand Down
1 change: 1 addition & 0 deletions src/Documentation/OpenStackSDK.content
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@
</HelpKeywords>
</Topic>
<Topic id="9c4a6074-9d84-4488-9565-50ecb5049ffe" visible="True" title="Preliminary Features" />
<Topic id="32cc6156-3b31-4450-b209-b55fcfc0a210" visible="True" title="Asynchronous Services" />
</Topics>
24 changes: 24 additions & 0 deletions src/corelib/Core/AsyncCompletionOption.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace net.openstack.Core
{
using System.Threading.Tasks;

/// <summary>
/// Specifies when a <see cref="Task"/> representing an asynchronous server operation
/// should be considered complete.
/// </summary>
/// <preliminary/>
public enum AsyncCompletionOption
{
/// <summary>
/// The <see cref="Task"/> representing the operation is considered complete after the
/// request has been submitted to the server.
/// </summary>
RequestSubmitted,

/// <summary>
/// The <see cref="Task"/> representing the operation is considered complete after the
/// server has completed processing the request.
/// </summary>
RequestCompleted,
}
}
22 changes: 22 additions & 0 deletions src/corelib/Core/IProgress`1.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace net.openstack.Core
{
/// <summary>
/// Defines a provider for progress updates.
/// </summary>
/// <typeparam name="T">The type of progress update value.</typeparam>
/// <preliminary/>
public interface IProgress<
#if NET35
T
#else
in T
#endif
>
{
/// <summary>
/// Reports a progress update.
/// </summary>
/// <param name="value">The value of the updated progress.</param>
void Report(T value);
}
}
57 changes: 57 additions & 0 deletions src/corelib/Core/InternalTaskExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
namespace net.openstack.Core
{
using System.Threading.Tasks;

/// <summary>
/// Provides extension methods to <see cref="Task"/> and <see cref="Task{TResult}"/> instances
/// for use within the openstack.net library.
/// </summary>
/// <threadsafety static="true" instance="false"/>
/// <preliminary/>
internal static class InternalTaskExtensions
{
/// <summary>
/// Gets a completed <see cref="Task"/>.
/// </summary>
/// <returns>A completed <see cref="Task"/>.</returns>
public static Task CompletedTask()
{
return CompletedTaskHolder.Default;
}

/// <summary>
/// Gets a completed <see cref="Task{TResult}"/> with the specified result.
/// </summary>
/// <typeparam name="TResult">The task result type.</typeparam>
/// <param name="result">The result of the completed task.</param>
/// <returns>A completed <see cref="Task{TResult}"/>, whose <see cref="Task{TResult}.Result"/> property returns the specified <paramref name="result"/>.</returns>
public static Task<TResult> CompletedTask<TResult>(TResult result)
{
TaskCompletionSource<TResult> completionSource = new TaskCompletionSource<TResult>();
completionSource.SetResult(result);
return completionSource.Task;
}

private static class CompletedTaskHolder
{
public static readonly Task Default;

static CompletedTaskHolder()
{
Default = CompletedTaskHolder<object>.Default;
}
}

private static class CompletedTaskHolder<T>
{
public static readonly Task<T> Default;

static CompletedTaskHolder()
{
TaskCompletionSource<T> completionSource = new TaskCompletionSource<T>();
completionSource.SetResult(default(T));
Default = completionSource.Task;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//--------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: TaskCompletionSourceExtensions.cs
//
//--------------------------------------------------------------------------

namespace System.Threading.Tasks
{
/// <summary>Extension methods for TaskCompletionSource.</summary>
/// <preliminary/>
internal static class TaskCompletionSourceExtensions
{
/// <summary>Transfers the result of a Task to the TaskCompletionSource.</summary>
/// <typeparam name="TResult">Specifies the type of the result.</typeparam>
/// <param name="resultSetter">The TaskCompletionSource.</param>
/// <param name="task">The task whose completion results should be transferred.</param>
public static void SetFromTask<TResult>(this TaskCompletionSource<TResult> resultSetter, Task task)
{
switch (task.Status)
{
case TaskStatus.RanToCompletion: resultSetter.SetResult(task is Task<TResult> ? ((Task<TResult>)task).Result : default(TResult)); break;
case TaskStatus.Faulted: resultSetter.SetException(task.Exception.InnerExceptions); break;
case TaskStatus.Canceled: resultSetter.SetCanceled(); break;
default: throw new InvalidOperationException("The task was not completed.");
}
}

/// <summary>Transfers the result of a Task to the TaskCompletionSource.</summary>
/// <typeparam name="TResult">Specifies the type of the result.</typeparam>
/// <param name="resultSetter">The TaskCompletionSource.</param>
/// <param name="task">The task whose completion results should be transferred.</param>
public static void SetFromTask<TResult>(this TaskCompletionSource<TResult> resultSetter, Task<TResult> task)
{
SetFromTask(resultSetter, (Task)task);
}

/// <summary>Attempts to transfer the result of a Task to the TaskCompletionSource.</summary>
/// <typeparam name="TResult">Specifies the type of the result.</typeparam>
/// <param name="resultSetter">The TaskCompletionSource.</param>
/// <param name="task">The task whose completion results should be transferred.</param>
/// <returns>Whether the transfer could be completed.</returns>
public static bool TrySetFromTask<TResult>(this TaskCompletionSource<TResult> resultSetter, Task task)
{
switch (task.Status)
{
case TaskStatus.RanToCompletion: return resultSetter.TrySetResult(task is Task<TResult> ? ((Task<TResult>)task).Result : default(TResult));
case TaskStatus.Faulted: return resultSetter.TrySetException(task.Exception.InnerExceptions);
case TaskStatus.Canceled: return resultSetter.TrySetCanceled();
default: throw new InvalidOperationException("The task was not completed.");
}
}

/// <summary>Attempts to transfer the result of a Task to the TaskCompletionSource.</summary>
/// <typeparam name="TResult">Specifies the type of the result.</typeparam>
/// <param name="resultSetter">The TaskCompletionSource.</param>
/// <param name="task">The task whose completion results should be transferred.</param>
/// <returns>Whether the transfer could be completed.</returns>
public static bool TrySetFromTask<TResult>(this TaskCompletionSource<TResult> resultSetter, Task<TResult> task)
{
return TrySetFromTask(resultSetter, (Task)task);
}
}
}
Loading