From 21970992963c6cadbe15e9dcb2f0729f47c98309 Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Thu, 27 Sep 2018 22:14:49 +0300 Subject: [PATCH 01/38] CHANGELOG update --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9f842b..b847e16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/); this project adheres to [Semantic Versioning](http://semver.org/). +----------------------- +## [0.9.8] - unreleased + ----------------------- ## [0.9.7] - 2018.09.27 From 28513d69c4b99ba92ca83c199b021c977940acd3 Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Fri, 28 Sep 2018 11:27:12 +0300 Subject: [PATCH 02/38] Minot optimizations --- .../Observable/AsyncObservableResult{T}.cs | 12 ++++-------- .../Implementation/Public/AsyncResult.Helpers.cs | 16 ++++++++-------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/UnityFx.Async/Implementation/Private/Observable/AsyncObservableResult{T}.cs b/src/UnityFx.Async/Implementation/Private/Observable/AsyncObservableResult{T}.cs index 709d3c9..7250c04 100644 --- a/src/UnityFx.Async/Implementation/Private/Observable/AsyncObservableResult{T}.cs +++ b/src/UnityFx.Async/Implementation/Private/Observable/AsyncObservableResult{T}.cs @@ -43,18 +43,14 @@ protected override void Dispose(bool disposing) void IObserver.OnNext(T value) { - if (TrySetResult(value, false)) - { - _subscription.Dispose(); - } + _subscription.Dispose(); + TrySetResult(value, false); } void IObserver.OnError(Exception error) { - if (TrySetException(error, false)) - { - _subscription.Dispose(); - } + _subscription.Dispose(); + TrySetException(error, false); } void IObserver.OnCompleted() diff --git a/src/UnityFx.Async/Implementation/Public/AsyncResult.Helpers.cs b/src/UnityFx.Async/Implementation/Public/AsyncResult.Helpers.cs index a33adc3..2f90e7d 100644 --- a/src/UnityFx.Async/Implementation/Public/AsyncResult.Helpers.cs +++ b/src/UnityFx.Async/Implementation/Public/AsyncResult.Helpers.cs @@ -513,22 +513,22 @@ public static AsyncResult FromTask(Task task) throw new ArgumentNullException(nameof(task)); } - var result = new AsyncCompletionSource(AsyncOperationStatus.Running); + var result = new AsyncResult(AsyncOperationStatus.Running); task.ContinueWith( t => { if (t.IsFaulted) { - result.SetException(t.Exception); + result.TrySetException(t.Exception); } else if (t.IsCanceled) { - result.SetCanceled(); + result.TrySetCanceled(); } else { - result.SetCompleted(); + result.TrySetCompleted(); } }, TaskContinuationOptions.ExecuteSynchronously); @@ -550,22 +550,22 @@ public static AsyncResult FromTask(Task task) throw new ArgumentNullException(nameof(task)); } - var result = new AsyncCompletionSource(AsyncOperationStatus.Running); + var result = new AsyncResult(AsyncOperationStatus.Running); task.ContinueWith( t => { if (t.IsFaulted) { - result.SetException(t.Exception); + result.TrySetException(t.Exception); } else if (t.IsCanceled) { - result.SetCanceled(); + result.TrySetCanceled(); } else { - result.SetResult(t.Result); + result.TrySetResult(t.Result); } }, TaskContinuationOptions.ExecuteSynchronously); From c33cb8137b4a3c90c14da170dc6ff5987e2c05c2 Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Fri, 28 Sep 2018 13:13:17 +0300 Subject: [PATCH 03/38] Added compiler stuff for Task-like types support in C# 7.2 (no actual implementation, just classes) --- .../AsyncMethodBuilderAttribute.cs | 32 +++++ .../AsyncResultMethodBuilder.cs | 128 +++++++++++++++++ .../AsyncResultMethodBuilder{TResult}.cs | 129 ++++++++++++++++++ .../AsyncResultVoidMethodBuilder.cs | 99 ++++++++++++++ 4 files changed, 388 insertions(+) create mode 100644 src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncMethodBuilderAttribute.cs create mode 100644 src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncResultMethodBuilder.cs create mode 100644 src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncResultMethodBuilder{TResult}.cs create mode 100644 src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncResultVoidMethodBuilder.cs diff --git a/src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncMethodBuilderAttribute.cs b/src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncMethodBuilderAttribute.cs new file mode 100644 index 0000000..dbae5df --- /dev/null +++ b/src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncMethodBuilderAttribute.cs @@ -0,0 +1,32 @@ +// Copyright (c) Alexander Bogarsukov. +// Licensed under the MIT license. See the LICENSE.md file in the project root for more information. + +using System; + +namespace System.Runtime.CompilerServices +{ +#if !NET35 + + /// + /// Indicates the type of the async method builder that should be used by a language compiler to + /// build the attributed type when used as the return type of an async method. + /// + /// + public sealed class AsyncMethodBuilderAttribute : Attribute + { + /// + /// Gets the of the associated builder. + /// + public Type BuilderType { get; } + + /// + /// Initializes a new instance of the class. + /// + public AsyncMethodBuilderAttribute(Type builderType) + { + BuilderType = builderType; + } + } + +#endif +} diff --git a/src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncResultMethodBuilder.cs b/src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncResultMethodBuilder.cs new file mode 100644 index 0000000..92d213f --- /dev/null +++ b/src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncResultMethodBuilder.cs @@ -0,0 +1,128 @@ +// Copyright (c) Alexander Bogarsukov. +// Licensed under the MIT license. See the LICENSE.md file in the project root for more information. + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace UnityFx.Async.CompilerServices +{ +#if !NET35 + + /// + /// Provides a builder for asynchronous methods that return . This type is intended for compiler use only. + /// + /// + /// is a value type, and thus it is copied by value. Prior to being copied, + /// one of its , , or members must be accessed, + /// or else the copies may end up building distinct instances. + /// + /// + /// + public struct AsyncResultMethodBuilder + { + private AsyncResult _op; + + /// + /// Initializes a new . + /// + /// The initialized . + [DebuggerHidden] + public static AsyncResultMethodBuilder Create() + { + return default(AsyncResultMethodBuilder); + } + + /// + /// Gets the for this builder. + /// + /// The representing the builder's asynchronous operation. + /// The builder is not initialized. + [DebuggerHidden] + public AsyncResult Task + { + get + { + if (_op == null) + { + _op = new AsyncResult(); + } + + return _op; + } + } + + /// + /// Completes the in the state. + /// + /// The builder is not initialized. + /// The operation has already completed. + [DebuggerHidden] + public void SetResult() + { + throw new NotImplementedException(); + } + + /// + /// Completes the in the state with the specified . + /// + /// The to use to fault the operation. + /// The is . + /// The builder is not initialized. + /// The operation has already completed. + [DebuggerHidden] + public void SetException(Exception exception) + { + throw new NotImplementedException(); + } + + /// + /// Initiates the builder's execution with the associated state machine. + /// + /// Specifies the type of the state machine. + /// The state machine instance, passed by reference. + [DebuggerHidden] + public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine + { + throw new NotImplementedException(); + } + + /// + /// Associates the builder with the state machine it represents. + /// + /// The heap-allocated state machine object. + [DebuggerHidden] + public void SetStateMachine(IAsyncStateMachine stateMachine) + { + throw new NotImplementedException(); + } + + /// + /// Schedules the specified state machine to be pushed forward when the specified awaiter completes. + /// + /// Specifies the type of the awaiter. + /// Specifies the type of the state machine. + /// The awaiter passed by reference. + /// The state machine passed by reference. + [DebuggerHidden] + public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine + { + throw new NotImplementedException(); + } + + /// + /// Schedules the specified state machine to be pushed forward when the specified awaiter completes. + /// + /// Specifies the type of the awaiter. + /// Specifies the type of the state machine. + /// The awaiter passed by reference. + /// The state machine passed by reference. + [DebuggerHidden] + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine + { + throw new NotImplementedException(); + } + } + +#endif +} diff --git a/src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncResultMethodBuilder{TResult}.cs b/src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncResultMethodBuilder{TResult}.cs new file mode 100644 index 0000000..d888e2f --- /dev/null +++ b/src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncResultMethodBuilder{TResult}.cs @@ -0,0 +1,129 @@ +// Copyright (c) Alexander Bogarsukov. +// Licensed under the MIT license. See the LICENSE.md file in the project root for more information. + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace UnityFx.Async.CompilerServices +{ +#if !NET35 + + /// + /// Provides a builder for asynchronous methods that return . This type is intended for compiler use only. + /// + /// + /// is a value type, and thus it is copied by value. Prior to being copied, + /// one of its , , or members must be accessed, + /// or else the copies may end up building distinct instances. + /// + /// + /// + public struct AsyncResultMethodBuilder + { + private AsyncResult _op; + + /// + /// Initializes a new . + /// + /// The initialized . + [DebuggerHidden] + public static AsyncResultMethodBuilder Create() + { + return default(AsyncResultMethodBuilder); + } + + /// + /// Gets the for this builder. + /// + /// The representing the builder's asynchronous operation. + /// The builder is not initialized. + [DebuggerHidden] + public AsyncResult Task + { + get + { + return _op; + } + } + + /// + /// Completes the in the state. + /// + /// The result to use to complete the operation. + /// The builder is not initialized. + /// The operation has already completed. + [DebuggerHidden] + public void SetResult(TResult result) + { + throw new NotImplementedException(); + } + + /// + /// Completes the in the state with the specified . + /// + /// The to use to fault the operation. + /// The is . + /// The builder is not initialized. + /// The operation has already completed. + [DebuggerHidden] + public void SetException(Exception exception) + { + throw new NotImplementedException(); + } + + /// + /// Initiates the builder's execution with the associated state machine. + /// + /// Specifies the type of the state machine. + /// The state machine instance, passed by reference. + [DebuggerHidden] + public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine + { + throw new NotImplementedException(); + } + + /// + /// Associates the builder with the state machine it represents. + /// + /// The heap-allocated state machine object. + [DebuggerHidden] + public void SetStateMachine(IAsyncStateMachine stateMachine) + { + throw new NotImplementedException(); + } + + /// + /// Schedules the specified state machine to be pushed forward when the specified awaiter completes. + /// + /// Specifies the type of the awaiter. + /// Specifies the type of the state machine. + /// The awaiter passed by reference. + /// The state machine passed by reference. + [DebuggerHidden] + public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine + { + throw new NotImplementedException(); + } + + /// + /// Schedules the specified state machine to be pushed forward when the specified awaiter completes. + /// + /// Specifies the type of the awaiter. + /// Specifies the type of the state machine. + /// The awaiter passed by reference. + /// The state machine passed by reference. + [DebuggerHidden] + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine + { + throw new NotImplementedException(); + } + + private AsyncResult GetTaskForResult(TResult result) + { + throw new NotImplementedException(); + } + } + +#endif +} diff --git a/src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncResultVoidMethodBuilder.cs b/src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncResultVoidMethodBuilder.cs new file mode 100644 index 0000000..5e73e86 --- /dev/null +++ b/src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncResultVoidMethodBuilder.cs @@ -0,0 +1,99 @@ +// Copyright (c) Alexander Bogarsukov. +// Licensed under the MIT license. See the LICENSE.md file in the project root for more information. + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace UnityFx.Async.CompilerServices +{ +#if !NET35 + + /// + /// Provides a builder for asynchronous methods that return . This type is intended for compiler use only. + /// + /// + /// + public struct AsyncResultVoidMethodBuilder + { + /// + /// Initializes a new . + /// + /// The initialized . + [DebuggerHidden] + public static AsyncResultMethodBuilder Create() + { + return default(AsyncResultMethodBuilder); + } + + /// + /// Completes the method builder successfully. + /// + [DebuggerHidden] + public void SetResult() + { + throw new NotImplementedException(); + } + + /// + /// Faults the method builder with an exception. + /// + /// The that is the cause of this fault. + /// The is . + /// The builder is not initialized. + [DebuggerHidden] + public void SetException(Exception exception) + { + throw new NotImplementedException(); + } + + /// + /// Initiates the builder's execution with the associated state machine. + /// + /// Specifies the type of the state machine. + /// The state machine instance, passed by reference. + [DebuggerHidden] + public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine + { + throw new NotImplementedException(); + } + + /// + /// Associates the builder with the state machine it represents. + /// + /// The heap-allocated state machine object. + [DebuggerHidden] + public void SetStateMachine(IAsyncStateMachine stateMachine) + { + throw new NotImplementedException(); + } + + /// + /// Schedules the specified state machine to be pushed forward when the specified awaiter completes. + /// + /// Specifies the type of the awaiter. + /// Specifies the type of the state machine. + /// The awaiter passed by reference. + /// The state machine passed by reference. + [DebuggerHidden] + public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine + { + throw new NotImplementedException(); + } + + /// + /// Schedules the specified state machine to be pushed forward when the specified awaiter completes. + /// + /// Specifies the type of the awaiter. + /// Specifies the type of the state machine. + /// The awaiter passed by reference. + /// The state machine passed by reference. + [DebuggerHidden] + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine + { + throw new NotImplementedException(); + } + } + +#endif +} From c5185529d309c080a69d85bf3fd7670702542923 Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Fri, 28 Sep 2018 18:58:04 +0300 Subject: [PATCH 04/38] README update --- README.md | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 020c8cd..a604625 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ NuGet | [![NuGet](https://img.shields.io/nuget/v/UnityFx.Async.svg)](https://www Github | [![GitHub release](https://img.shields.io/github/release/Arvtesh/UnityFx.Async.svg?logo=github)](https://github.com/Arvtesh/UnityFx.Async/releases) Unity Asset Store | [![Asynchronous operations for Unity](https://img.shields.io/badge/tools-v0.9.7-green.svg)](https://assetstore.unity.com/packages/tools/asynchronous-operations-for-unity-96696) -**Required Unity 5.4 or higher.** +**Requires Unity 5.4 or higher.** **If you enjoy using the library - please, [rate and review](https://assetstore.unity.com/packages/tools/asynchronous-operations-for-unity-96696) it on the Asset Store!** @@ -55,7 +55,7 @@ git submodule -q update --init The binaries are available as a [NuGet package](https://www.nuget.org/packages/UnityFx.Async). See [here](http://docs.nuget.org/docs/start-here/using-the-package-manager-console) for instructions on installing a package via nuget. One can also download them directly from [Github releases](https://github.com/Arvtesh/UnityFx.Async/releases). Unity3d users can import corresponding [Unity Asset Store package](https://assetstore.unity.com/packages/tools/asynchronous-operations-for-unity-96696) using the editor. ### Unity dependencies -The library core (`UnityFx.Async.dll`) does not depend on Unity and can be used in any .NET projects (via assembly or [NuGet](https://www.nuget.org/packages/UnityFx.Async) reference). All Unity-specific stuff depends on the core and is included in [Unity Asset Store package](https://assetstore.unity.com/packages/tools/asynchronous-operations-for-unity-96696). +The library core (`UnityFx.Async.dll`) does not depend on Unity and can be used in any .NET project (via assembly or [NuGet](https://www.nuget.org/packages/UnityFx.Async) reference). All Unity-specific stuff depends on the core and is included in [Asset Store package](https://assetstore.unity.com/packages/tools/asynchronous-operations-for-unity-96696). ## Understanding the concepts The topics below are just a quick summary of problems and the proposed solutions. For more details on the topic please see useful links at the end of this document. @@ -439,7 +439,7 @@ Most common way of creating own asynchronous operation is instantiating `AsyncCo * `AsyncResult`: an asynchronous operation without a result value. * `AsyncResult`: an asynchronous operation with a result value. -The sample code below demostrates creating a delay operation (in fact library provides one, this is just a simplified example): +The sample code below demostrates creating a delay operation (in fact the library provides one, this is just a simplified example): ```csharp public class TimerDelayResult : AsyncResult { @@ -479,10 +479,9 @@ public class TimerDelayResult : AsyncResult ``` ### Unity3d helpers -The library consists of 3 major parts: +As stated abovethe library include 2 main parts: * Core tools (defined in `UnityFx.Async.dll` assembly, do not depend on Unity3d); -* Unity3d-specific tools (defined as a collection of C# scripts located in `Assets/Plugins/UnityFx.Async` if installed as an Asset Store package, require Unity3d to compile/execute). -* Unity3d samples (defined as a collection of C# scripts located in `Assets/UnityFx.Async` if installed as an Asset Store package, require Unity3d to compile/execute). +* Unity3d-specific tools (defined as a collection of C# scripts if installed as an Asset Store package, require Unity3d to compile/execute). Everything described before (unless specified otherwise) does not require Unity and can be used in any application. The Unity-specific stuff is located in 3 classes: * `AsyncUtility`. Defines helper methods for accessing main thread in Unity, running coroutines without actually using a `MonoBehaviour` and waiting for native Unity asynchronous operations outside of coroutines. @@ -492,11 +491,11 @@ Everything described before (unless specified otherwise) does not require Unity For example, one can throw a few lines of code to be executed on a main thread using: ```csharp // Sends a delegate to the main thread and blocks calling thread until it is executed. -AsyncUtility.SendToMainThread(args => Debug.Log("On the main thread."), null); +AsyncUtility.SendToMainThread(() => Debug.Log("On the main thread.")); // Posts a delegate to the main thread and returns immediately. Returns an asynchronous operation that can be used to track the delegate execution. -AsyncUtility.PostToMainThread(args => Debug.Log("On the main thread."), null); +AsyncUtility.PostToMainThread(() => Debug.Log("On the main thread.")); // If calling thread is the main thread executes the delegate synchronously, otherwise posts it to the main thread. Returns an asynchronous operation that can be used to track the delegate execution. -AsyncUtility.InvokeOnMainThread(args => Debug.Log("On the main thread."), null); +AsyncUtility.InvokeOnMainThread(() => Debug.Log("On the main thread.")); ``` ## Comparison to .NET Tasks @@ -524,7 +523,7 @@ Please note that the library is NOT a replacement for [Tasks](https://docs.micro - An extendable [IAsyncResult](https://docs.microsoft.com/en-us/dotnet/api/system.iasyncresult) implementation is needed. ## Motivation -The project was initially created to help author with his [Unity3d](https://unity3d.com) projects. Unity's [AsyncOperation](https://docs.unity3d.com/ScriptReference/AsyncOperation.html) and similar can only be used in coroutines, cannot be extended and mostly do not return result or error information, .NET 3.5 does not provide much help either and even with .NET 4.6 support compatibility requirements often do not allow using [Tasks](https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task). When I caught myself writing the same asynchronous operation wrappers in each project I decided to share my experience to the best of human kind. +The project was initially created to help author with his [Unity3d](https://unity3d.com) projects. Unity's [AsyncOperation](https://docs.unity3d.com/ScriptReference/AsyncOperation.html) and similar can only be used in coroutines, cannot be extended and mostly do not return result or error information, .NET 3.5 does not provide much help either and even with .NET 4.6 support compatibility requirements often do not allow using [Tasks](https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task) (and they are quite expensive). When I caught myself writing the same asynchronous operation wrappers in each project I decided to share my experience to the best of human kind. ## Documentation Please see the links below for extended information on the product: @@ -554,7 +553,7 @@ The project uses [SemVer](https://semver.org/) versioning pattern. For the versi Please see the [![license](https://img.shields.io/github/license/Arvtesh/UnityFx.Async.svg)](LICENSE.md) for details. ## Acknowledgments -Working on this project is a great experience. Please see below list of sources of my inspiration (in no particular order): +Working on this project is a great experience. Please see below a list of my inspiration sources (in no particular order): * [.NET reference source](https://referencesource.microsoft.com/mscorlib/System/threading/Tasks/Task.cs.html). A great source of knowledge and good programming practices. * [C-Sharp-Promise](https://github.com/Real-Serious-Games/C-Sharp-Promise). Another great C# promise library with excellent documentation. * [UniRx](https://github.com/neuecc/UniRx). A deeply reworked [Rx.NET](https://github.com/Reactive-Extensions/Rx.NET) port to Unity. From d53076a810a80d72bfd6567c5e2c33ea59971c7f Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Mon, 1 Oct 2018 11:44:23 +0300 Subject: [PATCH 05/38] README update --- .../Assets/Plugins/UnityFx.Async/README.txt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/UnityFx.Async.AssetStore/Assets/Plugins/UnityFx.Async/README.txt b/src/UnityFx.Async.AssetStore/Assets/Plugins/UnityFx.Async/README.txt index 8302ce4..7633b7c 100644 --- a/src/UnityFx.Async.AssetStore/Assets/Plugins/UnityFx.Async/README.txt +++ b/src/UnityFx.Async.AssetStore/Assets/Plugins/UnityFx.Async/README.txt @@ -1,8 +1,5 @@ SUMMARY -The package provides lightweight Task-like asynchronous operations (promises) for Unity3d. - -PUBLISHER INFORMATION -https://www.linkedin.com/in/alexander-bogarsukov-93b9682/ +Lightweight Task-like asynchronous operations (promises) for Unity3d. LICENSE https://github.com/Arvtesh/UnityFx.Async/blob/master/LICENSE.md From 9f7a04d0b83cdf9484105fccd508308c3d8f19db Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Mon, 1 Oct 2018 18:52:45 +0300 Subject: [PATCH 06/38] Changed auto-generated file version used by AppVeyor --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 1b90810..38f57a8 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -35,7 +35,7 @@ dotnet_csproj: version: '$(GitVersion_MajorMinorPatch)' package_version: '$(GitVersion_NuGetVersionV2)' assembly_version: '$(GitVersion_AssemblySemVer)' - file_version: '$(GitVersion_MajorMinorPatch).$(GitVersion_CommitsSinceVersionSource)' + file_version: '$(GitVersion_AssemblySemVer)' informational_version: '$(GitVersion_InformationalVersion)' deploy: From 859fd8c6211e3a9f2ef2808ead606fd933aff198 Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Tue, 9 Oct 2018 21:44:09 +0300 Subject: [PATCH 07/38] Deprecated AsyncResultQueue --- .../Implementation/Public/Helpers/AsyncResultQueue{T}.cs | 1 + unity/Sandbox/Assets/Plugins.meta | 8 -------- 2 files changed, 1 insertion(+), 8 deletions(-) delete mode 100644 unity/Sandbox/Assets/Plugins.meta diff --git a/src/UnityFx.Async/Implementation/Public/Helpers/AsyncResultQueue{T}.cs b/src/UnityFx.Async/Implementation/Public/Helpers/AsyncResultQueue{T}.cs index b5d11c4..3d6d577 100644 --- a/src/UnityFx.Async/Implementation/Public/Helpers/AsyncResultQueue{T}.cs +++ b/src/UnityFx.Async/Implementation/Public/Helpers/AsyncResultQueue{T}.cs @@ -20,6 +20,7 @@ namespace UnityFx.Async /// /// /// + [Obsolete] #if NET35 public class AsyncResultQueue : IEnumerable, IAsyncCancellable where T : AsyncResult #else diff --git a/unity/Sandbox/Assets/Plugins.meta b/unity/Sandbox/Assets/Plugins.meta deleted file mode 100644 index cc089cd..0000000 --- a/unity/Sandbox/Assets/Plugins.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 23e3c25ef357701448d443d61ea39944 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: From d663077092af58ef52e05c3ec5cdbfe5a13b8b46 Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Tue, 9 Oct 2018 21:47:38 +0300 Subject: [PATCH 08/38] Fixed completion callbacks to be called even if OnCompleted/OnProgressChanged/OnStatusChanged throw --- src/UnityFx.Async/Implementation/Public/AsyncResult.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/UnityFx.Async/Implementation/Public/AsyncResult.cs b/src/UnityFx.Async/Implementation/Public/AsyncResult.cs index a3926ad..47d67ab 100644 --- a/src/UnityFx.Async/Implementation/Public/AsyncResult.cs +++ b/src/UnityFx.Async/Implementation/Public/AsyncResult.cs @@ -1339,12 +1339,11 @@ private void NotifyCompleted(AsyncOperationStatus status) OnProgressChanged(); OnStatusChanged(status); OnCompleted(); - - InvokeCallbacks(); } finally { _waitHandle?.Set(); + InvokeCallbacks(); } } From 96a75f78350812be5782f6320e217500a5a494a3 Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Tue, 9 Oct 2018 21:51:24 +0300 Subject: [PATCH 09/38] Added some comments --- .../Implementation/Public/AsyncResult.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/UnityFx.Async/Implementation/Public/AsyncResult.cs b/src/UnityFx.Async/Implementation/Public/AsyncResult.cs index 47d67ab..7f3b822 100644 --- a/src/UnityFx.Async/Implementation/Public/AsyncResult.cs +++ b/src/UnityFx.Async/Implementation/Public/AsyncResult.cs @@ -721,6 +721,9 @@ protected virtual float GetProgress() /// /// Called when the progress value has changed. Default implementation does nothing. /// + /// + /// Throwing an exception in this method results in unspecified behaviour. + /// /// /// /// @@ -732,6 +735,9 @@ protected virtual void OnProgressChanged() /// Called when the operation state has changed. Default implementation does nothing. /// /// The new status value. + /// + /// Throwing an exception in this method results in unspecified behaviour. + /// /// /// /// @@ -766,6 +772,9 @@ protected virtual void OnCancel() /// /// Called when the operation is completed. Default implementation does nothing. /// + /// + /// Throwing an exception in this method results in unspecified behaviour. + /// /// /// /// @@ -780,7 +789,7 @@ protected virtual void OnCompleted() /// Releases unmanaged resources used by the object. /// /// - /// Unlike most of the members of , this method is not thread-safe. + /// Unlike most of the members of , this method is not thread-safe. Do not throw exceptions in . /// /// A value that indicates whether this method is being called due to a call to . /// From 27c7a5e7a5654465d8c42d1a776020d960a892ed Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Wed, 10 Oct 2018 15:06:19 +0300 Subject: [PATCH 10/38] Flatterned project structure --- .../{Implementation/Public => Api}/AsyncCompletionSource.cs | 0 .../Public => Api}/AsyncCompletionSource{TResult}.cs | 0 .../{Implementation/Public => Api}/AsyncResult.Events.cs | 0 .../{Implementation/Public => Api}/AsyncResult.Helpers.cs | 0 src/UnityFx.Async/{Implementation/Public => Api}/AsyncResult.cs | 0 .../{Implementation/Public => Api}/AsyncResult{TResult}.cs | 0 .../Public => Api}/CompilerServices/AsyncAwaitable.cs | 0 .../Public => Api}/CompilerServices/AsyncAwaitable{TResult}.cs | 0 .../Public => Api}/CompilerServices/AsyncAwaiter.cs | 0 .../Public => Api}/CompilerServices/AsyncAwaiter{TResult}.cs | 0 .../CompilerServices/AsyncMethodBuilderAttribute.cs | 0 .../Public => Api}/CompilerServices/AsyncResultMethodBuilder.cs | 0 .../CompilerServices/AsyncResultMethodBuilder{TResult}.cs | 0 .../CompilerServices/AsyncResultVoidMethodBuilder.cs | 0 .../Public => Api}/Extensions/AsyncExtensions.Continuations.cs | 0 .../Public => Api}/Extensions/AsyncExtensions.Observables.cs | 0 .../Public => Api}/Extensions/AsyncExtensions.Tasks.cs | 0 .../Public => Api}/Extensions/AsyncExtensions.Wait.cs | 0 .../{Implementation/Public => Api}/Extensions/AsyncExtensions.cs | 0 .../{Implementation/Public => Api}/Helpers/AsyncLazy.cs | 0 .../{Implementation/Public => Api}/Helpers/AsyncResultQueue{T}.cs | 0 .../{Implementation/Public => Api}/Helpers/AsyncUpdateSource.cs | 0 .../Promises/AsyncExtensions.cs} | 0 .../Implementation/{Private/Common => }/AssemblyInfo.cs | 0 .../Implementation/{Private => }/Callbacks/CallbackData.cs | 0 .../Implementation/{Private => }/Callbacks/CallbackUtility.cs | 0 .../{Private => }/Callbacks/IAsyncCallbackCollection.cs | 0 .../{Private => }/Callbacks/ListCallbackCollection.cs | 0 .../{Private => }/Callbacks/MultiContextCallbackCollection.cs | 0 .../{Private => }/Callbacks/SingleContextCallbackCollection.cs | 0 src/UnityFx.Async/Implementation/{Private => }/Common/Messages.cs | 0 .../Implementation/{Private => }/Common/VoidResult.cs | 0 .../{Private => }/Continuations/ContinueWithResult{T,U}.cs | 0 .../Implementation/{Private => }/Continuations/UnwrapResult{T}.cs | 0 .../{Private => }/Observable/AsyncObservableResult{T}.cs | 0 .../{Private => }/Observable/AsyncObservableSubscription{T}.cs | 0 .../Continuations => }/Promises/CatchResult{T,TException}.cs | 0 .../{Private/Continuations => }/Promises/DoneResult{T}.cs | 0 .../{Private/Continuations => }/Promises/FinallyResult{T}.cs | 0 .../{Private/Continuations => }/Promises/RebindResult{T,U}.cs | 0 .../{Private/Continuations => }/Promises/ThenAllResult{T,U}.cs | 0 .../{Private/Continuations => }/Promises/ThenAnyResult{T,U}.cs | 0 .../{Private/Continuations => }/Promises/ThenResult{T,U}.cs | 0 .../Implementation/{Private => }/Specialized/RetryResult{T}.cs | 0 .../Implementation/{Private => }/Specialized/TimerDelayResult.cs | 0 .../{Private => }/Specialized/TimerRetryResult{T}.cs | 0 .../{Private => }/Specialized/UpdatableDelayResult.cs | 0 .../{Private => }/Specialized/UpdatableRetryResult{T}.cs | 0 .../Implementation/{Private => }/Specialized/WhenAllResult{T}.cs | 0 .../Implementation/{Private => }/Specialized/WhenAnyResult{T}.cs | 0 50 files changed, 0 insertions(+), 0 deletions(-) rename src/UnityFx.Async/{Implementation/Public => Api}/AsyncCompletionSource.cs (100%) rename src/UnityFx.Async/{Implementation/Public => Api}/AsyncCompletionSource{TResult}.cs (100%) rename src/UnityFx.Async/{Implementation/Public => Api}/AsyncResult.Events.cs (100%) rename src/UnityFx.Async/{Implementation/Public => Api}/AsyncResult.Helpers.cs (100%) rename src/UnityFx.Async/{Implementation/Public => Api}/AsyncResult.cs (100%) rename src/UnityFx.Async/{Implementation/Public => Api}/AsyncResult{TResult}.cs (100%) rename src/UnityFx.Async/{Implementation/Public => Api}/CompilerServices/AsyncAwaitable.cs (100%) rename src/UnityFx.Async/{Implementation/Public => Api}/CompilerServices/AsyncAwaitable{TResult}.cs (100%) rename src/UnityFx.Async/{Implementation/Public => Api}/CompilerServices/AsyncAwaiter.cs (100%) rename src/UnityFx.Async/{Implementation/Public => Api}/CompilerServices/AsyncAwaiter{TResult}.cs (100%) rename src/UnityFx.Async/{Implementation/Public => Api}/CompilerServices/AsyncMethodBuilderAttribute.cs (100%) rename src/UnityFx.Async/{Implementation/Public => Api}/CompilerServices/AsyncResultMethodBuilder.cs (100%) rename src/UnityFx.Async/{Implementation/Public => Api}/CompilerServices/AsyncResultMethodBuilder{TResult}.cs (100%) rename src/UnityFx.Async/{Implementation/Public => Api}/CompilerServices/AsyncResultVoidMethodBuilder.cs (100%) rename src/UnityFx.Async/{Implementation/Public => Api}/Extensions/AsyncExtensions.Continuations.cs (100%) rename src/UnityFx.Async/{Implementation/Public => Api}/Extensions/AsyncExtensions.Observables.cs (100%) rename src/UnityFx.Async/{Implementation/Public => Api}/Extensions/AsyncExtensions.Tasks.cs (100%) rename src/UnityFx.Async/{Implementation/Public => Api}/Extensions/AsyncExtensions.Wait.cs (100%) rename src/UnityFx.Async/{Implementation/Public => Api}/Extensions/AsyncExtensions.cs (100%) rename src/UnityFx.Async/{Implementation/Public => Api}/Helpers/AsyncLazy.cs (100%) rename src/UnityFx.Async/{Implementation/Public => Api}/Helpers/AsyncResultQueue{T}.cs (100%) rename src/UnityFx.Async/{Implementation/Public => Api}/Helpers/AsyncUpdateSource.cs (100%) rename src/UnityFx.Async/{Implementation/Public/Extensions/AsyncExtensions.Promises.cs => Api/Promises/AsyncExtensions.cs} (100%) rename src/UnityFx.Async/Implementation/{Private/Common => }/AssemblyInfo.cs (100%) rename src/UnityFx.Async/Implementation/{Private => }/Callbacks/CallbackData.cs (100%) rename src/UnityFx.Async/Implementation/{Private => }/Callbacks/CallbackUtility.cs (100%) rename src/UnityFx.Async/Implementation/{Private => }/Callbacks/IAsyncCallbackCollection.cs (100%) rename src/UnityFx.Async/Implementation/{Private => }/Callbacks/ListCallbackCollection.cs (100%) rename src/UnityFx.Async/Implementation/{Private => }/Callbacks/MultiContextCallbackCollection.cs (100%) rename src/UnityFx.Async/Implementation/{Private => }/Callbacks/SingleContextCallbackCollection.cs (100%) rename src/UnityFx.Async/Implementation/{Private => }/Common/Messages.cs (100%) rename src/UnityFx.Async/Implementation/{Private => }/Common/VoidResult.cs (100%) rename src/UnityFx.Async/Implementation/{Private => }/Continuations/ContinueWithResult{T,U}.cs (100%) rename src/UnityFx.Async/Implementation/{Private => }/Continuations/UnwrapResult{T}.cs (100%) rename src/UnityFx.Async/Implementation/{Private => }/Observable/AsyncObservableResult{T}.cs (100%) rename src/UnityFx.Async/Implementation/{Private => }/Observable/AsyncObservableSubscription{T}.cs (100%) rename src/UnityFx.Async/Implementation/{Private/Continuations => }/Promises/CatchResult{T,TException}.cs (100%) rename src/UnityFx.Async/Implementation/{Private/Continuations => }/Promises/DoneResult{T}.cs (100%) rename src/UnityFx.Async/Implementation/{Private/Continuations => }/Promises/FinallyResult{T}.cs (100%) rename src/UnityFx.Async/Implementation/{Private/Continuations => }/Promises/RebindResult{T,U}.cs (100%) rename src/UnityFx.Async/Implementation/{Private/Continuations => }/Promises/ThenAllResult{T,U}.cs (100%) rename src/UnityFx.Async/Implementation/{Private/Continuations => }/Promises/ThenAnyResult{T,U}.cs (100%) rename src/UnityFx.Async/Implementation/{Private/Continuations => }/Promises/ThenResult{T,U}.cs (100%) rename src/UnityFx.Async/Implementation/{Private => }/Specialized/RetryResult{T}.cs (100%) rename src/UnityFx.Async/Implementation/{Private => }/Specialized/TimerDelayResult.cs (100%) rename src/UnityFx.Async/Implementation/{Private => }/Specialized/TimerRetryResult{T}.cs (100%) rename src/UnityFx.Async/Implementation/{Private => }/Specialized/UpdatableDelayResult.cs (100%) rename src/UnityFx.Async/Implementation/{Private => }/Specialized/UpdatableRetryResult{T}.cs (100%) rename src/UnityFx.Async/Implementation/{Private => }/Specialized/WhenAllResult{T}.cs (100%) rename src/UnityFx.Async/Implementation/{Private => }/Specialized/WhenAnyResult{T}.cs (100%) diff --git a/src/UnityFx.Async/Implementation/Public/AsyncCompletionSource.cs b/src/UnityFx.Async/Api/AsyncCompletionSource.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Public/AsyncCompletionSource.cs rename to src/UnityFx.Async/Api/AsyncCompletionSource.cs diff --git a/src/UnityFx.Async/Implementation/Public/AsyncCompletionSource{TResult}.cs b/src/UnityFx.Async/Api/AsyncCompletionSource{TResult}.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Public/AsyncCompletionSource{TResult}.cs rename to src/UnityFx.Async/Api/AsyncCompletionSource{TResult}.cs diff --git a/src/UnityFx.Async/Implementation/Public/AsyncResult.Events.cs b/src/UnityFx.Async/Api/AsyncResult.Events.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Public/AsyncResult.Events.cs rename to src/UnityFx.Async/Api/AsyncResult.Events.cs diff --git a/src/UnityFx.Async/Implementation/Public/AsyncResult.Helpers.cs b/src/UnityFx.Async/Api/AsyncResult.Helpers.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Public/AsyncResult.Helpers.cs rename to src/UnityFx.Async/Api/AsyncResult.Helpers.cs diff --git a/src/UnityFx.Async/Implementation/Public/AsyncResult.cs b/src/UnityFx.Async/Api/AsyncResult.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Public/AsyncResult.cs rename to src/UnityFx.Async/Api/AsyncResult.cs diff --git a/src/UnityFx.Async/Implementation/Public/AsyncResult{TResult}.cs b/src/UnityFx.Async/Api/AsyncResult{TResult}.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Public/AsyncResult{TResult}.cs rename to src/UnityFx.Async/Api/AsyncResult{TResult}.cs diff --git a/src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncAwaitable.cs b/src/UnityFx.Async/Api/CompilerServices/AsyncAwaitable.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncAwaitable.cs rename to src/UnityFx.Async/Api/CompilerServices/AsyncAwaitable.cs diff --git a/src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncAwaitable{TResult}.cs b/src/UnityFx.Async/Api/CompilerServices/AsyncAwaitable{TResult}.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncAwaitable{TResult}.cs rename to src/UnityFx.Async/Api/CompilerServices/AsyncAwaitable{TResult}.cs diff --git a/src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncAwaiter.cs b/src/UnityFx.Async/Api/CompilerServices/AsyncAwaiter.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncAwaiter.cs rename to src/UnityFx.Async/Api/CompilerServices/AsyncAwaiter.cs diff --git a/src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncAwaiter{TResult}.cs b/src/UnityFx.Async/Api/CompilerServices/AsyncAwaiter{TResult}.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncAwaiter{TResult}.cs rename to src/UnityFx.Async/Api/CompilerServices/AsyncAwaiter{TResult}.cs diff --git a/src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncMethodBuilderAttribute.cs b/src/UnityFx.Async/Api/CompilerServices/AsyncMethodBuilderAttribute.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncMethodBuilderAttribute.cs rename to src/UnityFx.Async/Api/CompilerServices/AsyncMethodBuilderAttribute.cs diff --git a/src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncResultMethodBuilder.cs b/src/UnityFx.Async/Api/CompilerServices/AsyncResultMethodBuilder.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncResultMethodBuilder.cs rename to src/UnityFx.Async/Api/CompilerServices/AsyncResultMethodBuilder.cs diff --git a/src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncResultMethodBuilder{TResult}.cs b/src/UnityFx.Async/Api/CompilerServices/AsyncResultMethodBuilder{TResult}.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncResultMethodBuilder{TResult}.cs rename to src/UnityFx.Async/Api/CompilerServices/AsyncResultMethodBuilder{TResult}.cs diff --git a/src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncResultVoidMethodBuilder.cs b/src/UnityFx.Async/Api/CompilerServices/AsyncResultVoidMethodBuilder.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Public/CompilerServices/AsyncResultVoidMethodBuilder.cs rename to src/UnityFx.Async/Api/CompilerServices/AsyncResultVoidMethodBuilder.cs diff --git a/src/UnityFx.Async/Implementation/Public/Extensions/AsyncExtensions.Continuations.cs b/src/UnityFx.Async/Api/Extensions/AsyncExtensions.Continuations.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Public/Extensions/AsyncExtensions.Continuations.cs rename to src/UnityFx.Async/Api/Extensions/AsyncExtensions.Continuations.cs diff --git a/src/UnityFx.Async/Implementation/Public/Extensions/AsyncExtensions.Observables.cs b/src/UnityFx.Async/Api/Extensions/AsyncExtensions.Observables.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Public/Extensions/AsyncExtensions.Observables.cs rename to src/UnityFx.Async/Api/Extensions/AsyncExtensions.Observables.cs diff --git a/src/UnityFx.Async/Implementation/Public/Extensions/AsyncExtensions.Tasks.cs b/src/UnityFx.Async/Api/Extensions/AsyncExtensions.Tasks.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Public/Extensions/AsyncExtensions.Tasks.cs rename to src/UnityFx.Async/Api/Extensions/AsyncExtensions.Tasks.cs diff --git a/src/UnityFx.Async/Implementation/Public/Extensions/AsyncExtensions.Wait.cs b/src/UnityFx.Async/Api/Extensions/AsyncExtensions.Wait.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Public/Extensions/AsyncExtensions.Wait.cs rename to src/UnityFx.Async/Api/Extensions/AsyncExtensions.Wait.cs diff --git a/src/UnityFx.Async/Implementation/Public/Extensions/AsyncExtensions.cs b/src/UnityFx.Async/Api/Extensions/AsyncExtensions.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Public/Extensions/AsyncExtensions.cs rename to src/UnityFx.Async/Api/Extensions/AsyncExtensions.cs diff --git a/src/UnityFx.Async/Implementation/Public/Helpers/AsyncLazy.cs b/src/UnityFx.Async/Api/Helpers/AsyncLazy.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Public/Helpers/AsyncLazy.cs rename to src/UnityFx.Async/Api/Helpers/AsyncLazy.cs diff --git a/src/UnityFx.Async/Implementation/Public/Helpers/AsyncResultQueue{T}.cs b/src/UnityFx.Async/Api/Helpers/AsyncResultQueue{T}.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Public/Helpers/AsyncResultQueue{T}.cs rename to src/UnityFx.Async/Api/Helpers/AsyncResultQueue{T}.cs diff --git a/src/UnityFx.Async/Implementation/Public/Helpers/AsyncUpdateSource.cs b/src/UnityFx.Async/Api/Helpers/AsyncUpdateSource.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Public/Helpers/AsyncUpdateSource.cs rename to src/UnityFx.Async/Api/Helpers/AsyncUpdateSource.cs diff --git a/src/UnityFx.Async/Implementation/Public/Extensions/AsyncExtensions.Promises.cs b/src/UnityFx.Async/Api/Promises/AsyncExtensions.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Public/Extensions/AsyncExtensions.Promises.cs rename to src/UnityFx.Async/Api/Promises/AsyncExtensions.cs diff --git a/src/UnityFx.Async/Implementation/Private/Common/AssemblyInfo.cs b/src/UnityFx.Async/Implementation/AssemblyInfo.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Private/Common/AssemblyInfo.cs rename to src/UnityFx.Async/Implementation/AssemblyInfo.cs diff --git a/src/UnityFx.Async/Implementation/Private/Callbacks/CallbackData.cs b/src/UnityFx.Async/Implementation/Callbacks/CallbackData.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Private/Callbacks/CallbackData.cs rename to src/UnityFx.Async/Implementation/Callbacks/CallbackData.cs diff --git a/src/UnityFx.Async/Implementation/Private/Callbacks/CallbackUtility.cs b/src/UnityFx.Async/Implementation/Callbacks/CallbackUtility.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Private/Callbacks/CallbackUtility.cs rename to src/UnityFx.Async/Implementation/Callbacks/CallbackUtility.cs diff --git a/src/UnityFx.Async/Implementation/Private/Callbacks/IAsyncCallbackCollection.cs b/src/UnityFx.Async/Implementation/Callbacks/IAsyncCallbackCollection.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Private/Callbacks/IAsyncCallbackCollection.cs rename to src/UnityFx.Async/Implementation/Callbacks/IAsyncCallbackCollection.cs diff --git a/src/UnityFx.Async/Implementation/Private/Callbacks/ListCallbackCollection.cs b/src/UnityFx.Async/Implementation/Callbacks/ListCallbackCollection.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Private/Callbacks/ListCallbackCollection.cs rename to src/UnityFx.Async/Implementation/Callbacks/ListCallbackCollection.cs diff --git a/src/UnityFx.Async/Implementation/Private/Callbacks/MultiContextCallbackCollection.cs b/src/UnityFx.Async/Implementation/Callbacks/MultiContextCallbackCollection.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Private/Callbacks/MultiContextCallbackCollection.cs rename to src/UnityFx.Async/Implementation/Callbacks/MultiContextCallbackCollection.cs diff --git a/src/UnityFx.Async/Implementation/Private/Callbacks/SingleContextCallbackCollection.cs b/src/UnityFx.Async/Implementation/Callbacks/SingleContextCallbackCollection.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Private/Callbacks/SingleContextCallbackCollection.cs rename to src/UnityFx.Async/Implementation/Callbacks/SingleContextCallbackCollection.cs diff --git a/src/UnityFx.Async/Implementation/Private/Common/Messages.cs b/src/UnityFx.Async/Implementation/Common/Messages.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Private/Common/Messages.cs rename to src/UnityFx.Async/Implementation/Common/Messages.cs diff --git a/src/UnityFx.Async/Implementation/Private/Common/VoidResult.cs b/src/UnityFx.Async/Implementation/Common/VoidResult.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Private/Common/VoidResult.cs rename to src/UnityFx.Async/Implementation/Common/VoidResult.cs diff --git a/src/UnityFx.Async/Implementation/Private/Continuations/ContinueWithResult{T,U}.cs b/src/UnityFx.Async/Implementation/Continuations/ContinueWithResult{T,U}.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Private/Continuations/ContinueWithResult{T,U}.cs rename to src/UnityFx.Async/Implementation/Continuations/ContinueWithResult{T,U}.cs diff --git a/src/UnityFx.Async/Implementation/Private/Continuations/UnwrapResult{T}.cs b/src/UnityFx.Async/Implementation/Continuations/UnwrapResult{T}.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Private/Continuations/UnwrapResult{T}.cs rename to src/UnityFx.Async/Implementation/Continuations/UnwrapResult{T}.cs diff --git a/src/UnityFx.Async/Implementation/Private/Observable/AsyncObservableResult{T}.cs b/src/UnityFx.Async/Implementation/Observable/AsyncObservableResult{T}.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Private/Observable/AsyncObservableResult{T}.cs rename to src/UnityFx.Async/Implementation/Observable/AsyncObservableResult{T}.cs diff --git a/src/UnityFx.Async/Implementation/Private/Observable/AsyncObservableSubscription{T}.cs b/src/UnityFx.Async/Implementation/Observable/AsyncObservableSubscription{T}.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Private/Observable/AsyncObservableSubscription{T}.cs rename to src/UnityFx.Async/Implementation/Observable/AsyncObservableSubscription{T}.cs diff --git a/src/UnityFx.Async/Implementation/Private/Continuations/Promises/CatchResult{T,TException}.cs b/src/UnityFx.Async/Implementation/Promises/CatchResult{T,TException}.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Private/Continuations/Promises/CatchResult{T,TException}.cs rename to src/UnityFx.Async/Implementation/Promises/CatchResult{T,TException}.cs diff --git a/src/UnityFx.Async/Implementation/Private/Continuations/Promises/DoneResult{T}.cs b/src/UnityFx.Async/Implementation/Promises/DoneResult{T}.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Private/Continuations/Promises/DoneResult{T}.cs rename to src/UnityFx.Async/Implementation/Promises/DoneResult{T}.cs diff --git a/src/UnityFx.Async/Implementation/Private/Continuations/Promises/FinallyResult{T}.cs b/src/UnityFx.Async/Implementation/Promises/FinallyResult{T}.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Private/Continuations/Promises/FinallyResult{T}.cs rename to src/UnityFx.Async/Implementation/Promises/FinallyResult{T}.cs diff --git a/src/UnityFx.Async/Implementation/Private/Continuations/Promises/RebindResult{T,U}.cs b/src/UnityFx.Async/Implementation/Promises/RebindResult{T,U}.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Private/Continuations/Promises/RebindResult{T,U}.cs rename to src/UnityFx.Async/Implementation/Promises/RebindResult{T,U}.cs diff --git a/src/UnityFx.Async/Implementation/Private/Continuations/Promises/ThenAllResult{T,U}.cs b/src/UnityFx.Async/Implementation/Promises/ThenAllResult{T,U}.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Private/Continuations/Promises/ThenAllResult{T,U}.cs rename to src/UnityFx.Async/Implementation/Promises/ThenAllResult{T,U}.cs diff --git a/src/UnityFx.Async/Implementation/Private/Continuations/Promises/ThenAnyResult{T,U}.cs b/src/UnityFx.Async/Implementation/Promises/ThenAnyResult{T,U}.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Private/Continuations/Promises/ThenAnyResult{T,U}.cs rename to src/UnityFx.Async/Implementation/Promises/ThenAnyResult{T,U}.cs diff --git a/src/UnityFx.Async/Implementation/Private/Continuations/Promises/ThenResult{T,U}.cs b/src/UnityFx.Async/Implementation/Promises/ThenResult{T,U}.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Private/Continuations/Promises/ThenResult{T,U}.cs rename to src/UnityFx.Async/Implementation/Promises/ThenResult{T,U}.cs diff --git a/src/UnityFx.Async/Implementation/Private/Specialized/RetryResult{T}.cs b/src/UnityFx.Async/Implementation/Specialized/RetryResult{T}.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Private/Specialized/RetryResult{T}.cs rename to src/UnityFx.Async/Implementation/Specialized/RetryResult{T}.cs diff --git a/src/UnityFx.Async/Implementation/Private/Specialized/TimerDelayResult.cs b/src/UnityFx.Async/Implementation/Specialized/TimerDelayResult.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Private/Specialized/TimerDelayResult.cs rename to src/UnityFx.Async/Implementation/Specialized/TimerDelayResult.cs diff --git a/src/UnityFx.Async/Implementation/Private/Specialized/TimerRetryResult{T}.cs b/src/UnityFx.Async/Implementation/Specialized/TimerRetryResult{T}.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Private/Specialized/TimerRetryResult{T}.cs rename to src/UnityFx.Async/Implementation/Specialized/TimerRetryResult{T}.cs diff --git a/src/UnityFx.Async/Implementation/Private/Specialized/UpdatableDelayResult.cs b/src/UnityFx.Async/Implementation/Specialized/UpdatableDelayResult.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Private/Specialized/UpdatableDelayResult.cs rename to src/UnityFx.Async/Implementation/Specialized/UpdatableDelayResult.cs diff --git a/src/UnityFx.Async/Implementation/Private/Specialized/UpdatableRetryResult{T}.cs b/src/UnityFx.Async/Implementation/Specialized/UpdatableRetryResult{T}.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Private/Specialized/UpdatableRetryResult{T}.cs rename to src/UnityFx.Async/Implementation/Specialized/UpdatableRetryResult{T}.cs diff --git a/src/UnityFx.Async/Implementation/Private/Specialized/WhenAllResult{T}.cs b/src/UnityFx.Async/Implementation/Specialized/WhenAllResult{T}.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Private/Specialized/WhenAllResult{T}.cs rename to src/UnityFx.Async/Implementation/Specialized/WhenAllResult{T}.cs diff --git a/src/UnityFx.Async/Implementation/Private/Specialized/WhenAnyResult{T}.cs b/src/UnityFx.Async/Implementation/Specialized/WhenAnyResult{T}.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Private/Specialized/WhenAnyResult{T}.cs rename to src/UnityFx.Async/Implementation/Specialized/WhenAnyResult{T}.cs From 989c8db28421d2c4b04e9a85db2c2e3a3a06a0fa Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Wed, 10 Oct 2018 15:55:46 +0300 Subject: [PATCH 11/38] Project structure changes --- src/UnityFx.Async/Api/AsyncResult.Helpers.cs | 2 +- src/UnityFx.Async/Api/AsyncResult{TResult}.cs | 2 +- .../Api/Extensions/AsyncExtensions.Observables.cs | 2 +- .../{Continuations => Specialized}/ContinueWithResult{T,U}.cs | 0 .../FromObservableResult{T}.cs} | 4 ++-- .../ObservableSubscription{T}.cs} | 4 ++-- .../{Continuations => Specialized}/UnwrapResult{T}.cs | 0 7 files changed, 7 insertions(+), 7 deletions(-) rename src/UnityFx.Async/Implementation/{Continuations => Specialized}/ContinueWithResult{T,U}.cs (100%) rename src/UnityFx.Async/Implementation/{Observable/AsyncObservableResult{T}.cs => Specialized/FromObservableResult{T}.cs} (86%) rename src/UnityFx.Async/Implementation/{Observable/AsyncObservableSubscription{T}.cs => Specialized/ObservableSubscription{T}.cs} (82%) rename src/UnityFx.Async/Implementation/{Continuations => Specialized}/UnwrapResult{T}.cs (100%) diff --git a/src/UnityFx.Async/Api/AsyncResult.Helpers.cs b/src/UnityFx.Async/Api/AsyncResult.Helpers.cs index 2f90e7d..9c5078f 100644 --- a/src/UnityFx.Async/Api/AsyncResult.Helpers.cs +++ b/src/UnityFx.Async/Api/AsyncResult.Helpers.cs @@ -623,7 +623,7 @@ public static AsyncResult FromObservable(IObservable observable) throw new ArgumentNullException(nameof(observable)); } - return new AsyncObservableResult(observable); + return new FromObservableResult(observable); } #endif diff --git a/src/UnityFx.Async/Api/AsyncResult{TResult}.cs b/src/UnityFx.Async/Api/AsyncResult{TResult}.cs index fccb6bb..a3bb451 100644 --- a/src/UnityFx.Async/Api/AsyncResult{TResult}.cs +++ b/src/UnityFx.Async/Api/AsyncResult{TResult}.cs @@ -308,7 +308,7 @@ public IDisposable Subscribe(IObserver observer) throw new ArgumentNullException(nameof(observer)); } - var result = new AsyncObservableSubscription(this, observer); + var result = new ObservableSubscription(this, observer); AddCompletionCallback(result, null); return result; } diff --git a/src/UnityFx.Async/Api/Extensions/AsyncExtensions.Observables.cs b/src/UnityFx.Async/Api/Extensions/AsyncExtensions.Observables.cs index 5bc6a72..e052297 100644 --- a/src/UnityFx.Async/Api/Extensions/AsyncExtensions.Observables.cs +++ b/src/UnityFx.Async/Api/Extensions/AsyncExtensions.Observables.cs @@ -17,7 +17,7 @@ partial class AsyncExtensions /// Returns an instance that can be used to track the observable. public static IAsyncOperation ToAsync(this IObservable observable) { - return new AsyncObservableResult(observable); + return new FromObservableResult(observable); } } diff --git a/src/UnityFx.Async/Implementation/Continuations/ContinueWithResult{T,U}.cs b/src/UnityFx.Async/Implementation/Specialized/ContinueWithResult{T,U}.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Continuations/ContinueWithResult{T,U}.cs rename to src/UnityFx.Async/Implementation/Specialized/ContinueWithResult{T,U}.cs diff --git a/src/UnityFx.Async/Implementation/Observable/AsyncObservableResult{T}.cs b/src/UnityFx.Async/Implementation/Specialized/FromObservableResult{T}.cs similarity index 86% rename from src/UnityFx.Async/Implementation/Observable/AsyncObservableResult{T}.cs rename to src/UnityFx.Async/Implementation/Specialized/FromObservableResult{T}.cs index 7250c04..b1489cd 100644 --- a/src/UnityFx.Async/Implementation/Observable/AsyncObservableResult{T}.cs +++ b/src/UnityFx.Async/Implementation/Specialized/FromObservableResult{T}.cs @@ -7,7 +7,7 @@ namespace UnityFx.Async { #if !NET35 - internal sealed class AsyncObservableResult : AsyncResult, IObserver + internal sealed class FromObservableResult : AsyncResult, IObserver { #region data @@ -17,7 +17,7 @@ internal sealed class AsyncObservableResult : AsyncResult, IObserver #region interface - internal AsyncObservableResult(IObservable observable) + internal FromObservableResult(IObservable observable) : base(AsyncOperationStatus.Running) { _subscription = observable.Subscribe(this); diff --git a/src/UnityFx.Async/Implementation/Observable/AsyncObservableSubscription{T}.cs b/src/UnityFx.Async/Implementation/Specialized/ObservableSubscription{T}.cs similarity index 82% rename from src/UnityFx.Async/Implementation/Observable/AsyncObservableSubscription{T}.cs rename to src/UnityFx.Async/Implementation/Specialized/ObservableSubscription{T}.cs index 1e5c171..93d2436 100644 --- a/src/UnityFx.Async/Implementation/Observable/AsyncObservableSubscription{T}.cs +++ b/src/UnityFx.Async/Implementation/Specialized/ObservableSubscription{T}.cs @@ -7,7 +7,7 @@ namespace UnityFx.Async { #if !NET35 - internal class AsyncObservableSubscription : IAsyncContinuation, IDisposable + internal class ObservableSubscription : IAsyncContinuation, IDisposable { #region data @@ -18,7 +18,7 @@ internal class AsyncObservableSubscription : IAsyncContinuation, IDisposable #region interface - public AsyncObservableSubscription(IAsyncOperation op, IObserver observer) + public ObservableSubscription(IAsyncOperation op, IObserver observer) { _op = op; _observer = observer; diff --git a/src/UnityFx.Async/Implementation/Continuations/UnwrapResult{T}.cs b/src/UnityFx.Async/Implementation/Specialized/UnwrapResult{T}.cs similarity index 100% rename from src/UnityFx.Async/Implementation/Continuations/UnwrapResult{T}.cs rename to src/UnityFx.Async/Implementation/Specialized/UnwrapResult{T}.cs From 97fac14529c86aab1a52f69f70d3c2501e0119a8 Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Wed, 10 Oct 2018 15:59:00 +0300 Subject: [PATCH 12/38] CHANGELOG update --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b847e16..eba06b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/); this proj ----------------------- ## [0.9.8] - unreleased +### Fixed +- Fixed `AsyncResult` completion callbacks to be called event if `OnCompleted` throws. + +### Removed +- Deprecated `AsyncResultQueue`. + ----------------------- ## [0.9.7] - 2018.09.27 From b7dad61714fe170b496d636ee7c726512d6fa15c Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Wed, 10 Oct 2018 18:32:20 +0300 Subject: [PATCH 13/38] Implemented compiler services to enable AsyncResult/AsyncResult as Task-like types --- src/UnityFx.Async/Api/AsyncResult.cs | 4 + src/UnityFx.Async/Api/AsyncResult{TResult}.cs | 3 + .../AsyncResultMethodBuilder.cs | 84 +++++++++++++--- .../AsyncResultMethodBuilder{TResult}.cs | 86 ++++++++++++---- .../AsyncResultVoidMethodBuilder.cs | 99 ------------------- 5 files changed, 145 insertions(+), 131 deletions(-) delete mode 100644 src/UnityFx.Async/Api/CompilerServices/AsyncResultVoidMethodBuilder.cs diff --git a/src/UnityFx.Async/Api/AsyncResult.cs b/src/UnityFx.Async/Api/AsyncResult.cs index 7f3b822..89dd2f1 100644 --- a/src/UnityFx.Async/Api/AsyncResult.cs +++ b/src/UnityFx.Async/Api/AsyncResult.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Globalization; using System.Linq; +using System.Runtime.CompilerServices; #if !NET35 using System.Runtime.ExceptionServices; #endif @@ -38,6 +39,9 @@ namespace UnityFx.Async /// /// [DebuggerDisplay("{DebuggerDisplay,nq}")] +#if !NET35 + [AsyncMethodBuilder(typeof(CompilerServices.AsyncResultMethodBuilder))] +#endif public partial class AsyncResult : IAsyncOperation, IAsyncContinuation, IEnumerator { #region data diff --git a/src/UnityFx.Async/Api/AsyncResult{TResult}.cs b/src/UnityFx.Async/Api/AsyncResult{TResult}.cs index a3bb451..6852b28 100644 --- a/src/UnityFx.Async/Api/AsyncResult{TResult}.cs +++ b/src/UnityFx.Async/Api/AsyncResult{TResult}.cs @@ -17,6 +17,9 @@ namespace UnityFx.Async /// Type of the operation result value. /// /// +#if !NET35 + [AsyncMethodBuilder(typeof(CompilerServices.AsyncResultMethodBuilder<>))] +#endif public class AsyncResult : AsyncResult, IAsyncOperation { #region data diff --git a/src/UnityFx.Async/Api/CompilerServices/AsyncResultMethodBuilder.cs b/src/UnityFx.Async/Api/CompilerServices/AsyncResultMethodBuilder.cs index 92d213f..0c68ec8 100644 --- a/src/UnityFx.Async/Api/CompilerServices/AsyncResultMethodBuilder.cs +++ b/src/UnityFx.Async/Api/CompilerServices/AsyncResultMethodBuilder.cs @@ -22,15 +22,22 @@ namespace UnityFx.Async.CompilerServices public struct AsyncResultMethodBuilder { private AsyncResult _op; + private Action _continuation; - /// - /// Initializes a new . - /// - /// The initialized . - [DebuggerHidden] - public static AsyncResultMethodBuilder Create() + internal class MoveNextRunner where TStateMachine : IAsyncStateMachine { - return default(AsyncResultMethodBuilder); + private TStateMachine _stateMachine; + + public void SetStateMachine(ref TStateMachine stateMachine) + { + _stateMachine = stateMachine; + } + + [DebuggerHidden] + public void Run() + { + _stateMachine.MoveNext(); + } } /// @@ -43,6 +50,11 @@ public AsyncResult Task { get { + if (_continuation == null) + { + return AsyncResult.CompletedOperation; + } + if (_op == null) { _op = new AsyncResult(); @@ -60,7 +72,14 @@ public AsyncResult Task [DebuggerHidden] public void SetResult() { - throw new NotImplementedException(); + if (_op == null) + { + _op = AsyncResult.CompletedOperation; + } + else if (!_op.TrySetCompleted()) + { + throw new InvalidOperationException(); + } } /// @@ -73,7 +92,14 @@ public void SetResult() [DebuggerHidden] public void SetException(Exception exception) { - throw new NotImplementedException(); + if (_op == null) + { + _op = AsyncResult.FromException(exception); + } + else if (!_op.TrySetException(exception)) + { + throw new InvalidOperationException(); + } } /// @@ -84,7 +110,7 @@ public void SetException(Exception exception) [DebuggerHidden] public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine { - throw new NotImplementedException(); + stateMachine.MoveNext(); } /// @@ -94,7 +120,6 @@ public void Start(ref TStateMachine stateMachine) where TStateMac [DebuggerHidden] public void SetStateMachine(IAsyncStateMachine stateMachine) { - throw new NotImplementedException(); } /// @@ -107,7 +132,20 @@ public void SetStateMachine(IAsyncStateMachine stateMachine) [DebuggerHidden] public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine { - throw new NotImplementedException(); + // Box the stateMachine into MoveNextRunner on first await. + if (_continuation == null) + { + // MoveNextRunner acts as a heap-allocated shell of the state machine. + var runner = new MoveNextRunner(); + + // Create the continuation delegate. + _continuation = runner.Run; + + // Copy the state machine into the runner (boxing). This should be done after _continuation is initialized. + runner.SetStateMachine(ref stateMachine); + } + + awaiter.OnCompleted(_continuation); } /// @@ -118,9 +156,27 @@ public void AwaitOnCompleted(ref TAwaiter awaiter, ref /// The awaiter passed by reference. /// The state machine passed by reference. [DebuggerHidden] - public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine { - throw new NotImplementedException(); + // Box the stateMachine into MoveNextRunner on first await. See AwaitOnCompleted for detailed comments. + if (_continuation == null) + { + var runner = new MoveNextRunner(); + _continuation = runner.Run; + runner.SetStateMachine(ref stateMachine); + } + + awaiter.UnsafeOnCompleted(_continuation); + } + + /// + /// Initializes a new . + /// + /// The initialized . + [DebuggerHidden] + public static AsyncResultMethodBuilder Create() + { + return default(AsyncResultMethodBuilder); } } diff --git a/src/UnityFx.Async/Api/CompilerServices/AsyncResultMethodBuilder{TResult}.cs b/src/UnityFx.Async/Api/CompilerServices/AsyncResultMethodBuilder{TResult}.cs index d888e2f..f6959f8 100644 --- a/src/UnityFx.Async/Api/CompilerServices/AsyncResultMethodBuilder{TResult}.cs +++ b/src/UnityFx.Async/Api/CompilerServices/AsyncResultMethodBuilder{TResult}.cs @@ -22,16 +22,8 @@ namespace UnityFx.Async.CompilerServices public struct AsyncResultMethodBuilder { private AsyncResult _op; - - /// - /// Initializes a new . - /// - /// The initialized . - [DebuggerHidden] - public static AsyncResultMethodBuilder Create() - { - return default(AsyncResultMethodBuilder); - } + private Action _continuation; + private TResult _result; /// /// Gets the for this builder. @@ -43,6 +35,16 @@ public AsyncResult Task { get { + if (_continuation == null) + { + return GetTaskForResult(_result); + } + + if (_op == null) + { + _op = new AsyncResult(); + } + return _op; } } @@ -56,7 +58,18 @@ public AsyncResult Task [DebuggerHidden] public void SetResult(TResult result) { - throw new NotImplementedException(); + if (_continuation == null) + { + _result = result; + } + else if (_op == null) + { + _op = GetTaskForResult(result); + } + else if (!_op.TrySetResult(result)) + { + throw new InvalidOperationException(); + } } /// @@ -69,7 +82,14 @@ public void SetResult(TResult result) [DebuggerHidden] public void SetException(Exception exception) { - throw new NotImplementedException(); + if (_op == null) + { + _op = AsyncResult.FromException(exception); + } + else if (!_op.TrySetException(exception)) + { + throw new InvalidOperationException(); + } } /// @@ -80,7 +100,7 @@ public void SetException(Exception exception) [DebuggerHidden] public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine { - throw new NotImplementedException(); + stateMachine.MoveNext(); } /// @@ -90,7 +110,6 @@ public void Start(ref TStateMachine stateMachine) where TStateMac [DebuggerHidden] public void SetStateMachine(IAsyncStateMachine stateMachine) { - throw new NotImplementedException(); } /// @@ -103,7 +122,20 @@ public void SetStateMachine(IAsyncStateMachine stateMachine) [DebuggerHidden] public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine { - throw new NotImplementedException(); + // Box the stateMachine into MoveNextRunner on first await. + if (_continuation == null) + { + // MoveNextRunner acts as a heap-allocated shell of the state machine. + var runner = new AsyncResultMethodBuilder.MoveNextRunner(); + + // Create the continuation delegate. + _continuation = runner.Run; + + // Copy the state machine into the runner (boxing). This should be done after _continuation is initialized. + runner.SetStateMachine(ref stateMachine); + } + + awaiter.OnCompleted(_continuation); } /// @@ -114,14 +146,32 @@ public void AwaitOnCompleted(ref TAwaiter awaiter, ref /// The awaiter passed by reference. /// The state machine passed by reference. [DebuggerHidden] - public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine + { + // Box the stateMachine into MoveNextRunner on first await. See AwaitOnCompleted for detailed comments. + if (_continuation == null) + { + var runner = new AsyncResultMethodBuilder.MoveNextRunner(); + _continuation = runner.Run; + runner.SetStateMachine(ref stateMachine); + } + + awaiter.UnsafeOnCompleted(_continuation); + } + + /// + /// Initializes a new . + /// + /// The initialized . + [DebuggerHidden] + public static AsyncResultMethodBuilder Create() { - throw new NotImplementedException(); + return default(AsyncResultMethodBuilder); } private AsyncResult GetTaskForResult(TResult result) { - throw new NotImplementedException(); + return AsyncResult.FromResult(result); } } diff --git a/src/UnityFx.Async/Api/CompilerServices/AsyncResultVoidMethodBuilder.cs b/src/UnityFx.Async/Api/CompilerServices/AsyncResultVoidMethodBuilder.cs deleted file mode 100644 index 5e73e86..0000000 --- a/src/UnityFx.Async/Api/CompilerServices/AsyncResultVoidMethodBuilder.cs +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) Alexander Bogarsukov. -// Licensed under the MIT license. See the LICENSE.md file in the project root for more information. - -using System; -using System.Diagnostics; -using System.Runtime.CompilerServices; - -namespace UnityFx.Async.CompilerServices -{ -#if !NET35 - - /// - /// Provides a builder for asynchronous methods that return . This type is intended for compiler use only. - /// - /// - /// - public struct AsyncResultVoidMethodBuilder - { - /// - /// Initializes a new . - /// - /// The initialized . - [DebuggerHidden] - public static AsyncResultMethodBuilder Create() - { - return default(AsyncResultMethodBuilder); - } - - /// - /// Completes the method builder successfully. - /// - [DebuggerHidden] - public void SetResult() - { - throw new NotImplementedException(); - } - - /// - /// Faults the method builder with an exception. - /// - /// The that is the cause of this fault. - /// The is . - /// The builder is not initialized. - [DebuggerHidden] - public void SetException(Exception exception) - { - throw new NotImplementedException(); - } - - /// - /// Initiates the builder's execution with the associated state machine. - /// - /// Specifies the type of the state machine. - /// The state machine instance, passed by reference. - [DebuggerHidden] - public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine - { - throw new NotImplementedException(); - } - - /// - /// Associates the builder with the state machine it represents. - /// - /// The heap-allocated state machine object. - [DebuggerHidden] - public void SetStateMachine(IAsyncStateMachine stateMachine) - { - throw new NotImplementedException(); - } - - /// - /// Schedules the specified state machine to be pushed forward when the specified awaiter completes. - /// - /// Specifies the type of the awaiter. - /// Specifies the type of the state machine. - /// The awaiter passed by reference. - /// The state machine passed by reference. - [DebuggerHidden] - public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine - { - throw new NotImplementedException(); - } - - /// - /// Schedules the specified state machine to be pushed forward when the specified awaiter completes. - /// - /// Specifies the type of the awaiter. - /// Specifies the type of the state machine. - /// The awaiter passed by reference. - /// The state machine passed by reference. - [DebuggerHidden] - public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine - { - throw new NotImplementedException(); - } - } - -#endif -} From 0c71a52207d2a5974984351eb704ded545dbd014 Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Thu, 11 Oct 2018 13:03:03 +0300 Subject: [PATCH 14/38] AsyncMethodBuilder refactoring --- .../AsyncResultMethodBuilder.cs | 90 ++++++++++++------- .../AsyncResultMethodBuilder{TResult}.cs | 85 ++++++++++++------ 2 files changed, 117 insertions(+), 58 deletions(-) diff --git a/src/UnityFx.Async/Api/CompilerServices/AsyncResultMethodBuilder.cs b/src/UnityFx.Async/Api/CompilerServices/AsyncResultMethodBuilder.cs index 0c68ec8..9400a14 100644 --- a/src/UnityFx.Async/Api/CompilerServices/AsyncResultMethodBuilder.cs +++ b/src/UnityFx.Async/Api/CompilerServices/AsyncResultMethodBuilder.cs @@ -18,27 +18,16 @@ namespace UnityFx.Async.CompilerServices /// or else the copies may end up building distinct instances. /// /// - /// public struct AsyncResultMethodBuilder { + #region data + private AsyncResult _op; private Action _continuation; - internal class MoveNextRunner where TStateMachine : IAsyncStateMachine - { - private TStateMachine _stateMachine; - - public void SetStateMachine(ref TStateMachine stateMachine) - { - _stateMachine = stateMachine; - } + #endregion - [DebuggerHidden] - public void Run() - { - _stateMachine.MoveNext(); - } - } + #region interface /// /// Gets the for this builder. @@ -50,11 +39,7 @@ public AsyncResult Task { get { - if (_continuation == null) - { - return AsyncResult.CompletedOperation; - } - + // Is this code really needed? _op should always be initialized when this is called. if (_op == null) { _op = new AsyncResult(); @@ -69,6 +54,7 @@ public AsyncResult Task /// /// The builder is not initialized. /// The operation has already completed. + /// [DebuggerHidden] public void SetResult() { @@ -89,6 +75,7 @@ public void SetResult() /// The is . /// The builder is not initialized. /// The operation has already completed. + /// [DebuggerHidden] public void SetException(Exception exception) { @@ -129,20 +116,13 @@ public void SetStateMachine(IAsyncStateMachine stateMachine) /// Specifies the type of the state machine. /// The awaiter passed by reference. /// The state machine passed by reference. + /// [DebuggerHidden] public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine { - // Box the stateMachine into MoveNextRunner on first await. if (_continuation == null) { - // MoveNextRunner acts as a heap-allocated shell of the state machine. - var runner = new MoveNextRunner(); - - // Create the continuation delegate. - _continuation = runner.Run; - - // Copy the state machine into the runner (boxing). This should be done after _continuation is initialized. - runner.SetStateMachine(ref stateMachine); + OnFirstAwait(ref stateMachine); } awaiter.OnCompleted(_continuation); @@ -155,15 +135,13 @@ public void AwaitOnCompleted(ref TAwaiter awaiter, ref /// Specifies the type of the state machine. /// The awaiter passed by reference. /// The state machine passed by reference. + /// [DebuggerHidden] public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine { - // Box the stateMachine into MoveNextRunner on first await. See AwaitOnCompleted for detailed comments. if (_continuation == null) { - var runner = new MoveNextRunner(); - _continuation = runner.Run; - runner.SetStateMachine(ref stateMachine); + OnFirstAwait(ref stateMachine); } awaiter.UnsafeOnCompleted(_continuation); @@ -178,6 +156,52 @@ public static AsyncResultMethodBuilder Create() { return default(AsyncResultMethodBuilder); } + + #endregion + + #region implementation + + private class MoveNextRunner : AsyncResult where TStateMachine : IAsyncStateMachine + { + private TStateMachine _stateMachine; + + public MoveNextRunner() + : base(AsyncOperationStatus.Running) + { + } + + public void SetStateMachine(ref TStateMachine stateMachine) + { + _stateMachine = stateMachine; + } + + [DebuggerHidden] + public void Run() + { + Debug.Assert(!IsCompleted); + _stateMachine.MoveNext(); + } + } + + /// + /// First await handler. Boxes the and initializes continuation action. + /// + /// Type of the state machine instance. + /// The parent state machine. + private void OnFirstAwait(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine + { + // MoveNextRunner acts as a heap-allocated shell of the state machine + task. + var runner = new MoveNextRunner(); + + // Init all members references so they are shared between this instance and the boxed one. + _continuation = runner.Run; + _op = new AsyncResult(AsyncOperationStatus.Running); + + // Copy the state machine into the runner (boxing). This should be done after _continuation and _op is initialized. + runner.SetStateMachine(ref stateMachine); + } + + #endregion } #endif diff --git a/src/UnityFx.Async/Api/CompilerServices/AsyncResultMethodBuilder{TResult}.cs b/src/UnityFx.Async/Api/CompilerServices/AsyncResultMethodBuilder{TResult}.cs index f6959f8..ec5bc38 100644 --- a/src/UnityFx.Async/Api/CompilerServices/AsyncResultMethodBuilder{TResult}.cs +++ b/src/UnityFx.Async/Api/CompilerServices/AsyncResultMethodBuilder{TResult}.cs @@ -18,12 +18,16 @@ namespace UnityFx.Async.CompilerServices /// or else the copies may end up building distinct instances. /// /// - /// public struct AsyncResultMethodBuilder { + #region data + private AsyncResult _op; private Action _continuation; - private TResult _result; + + #endregion + + #region interface /// /// Gets the for this builder. @@ -35,11 +39,6 @@ public AsyncResult Task { get { - if (_continuation == null) - { - return GetTaskForResult(_result); - } - if (_op == null) { _op = new AsyncResult(); @@ -58,11 +57,7 @@ public AsyncResult Task [DebuggerHidden] public void SetResult(TResult result) { - if (_continuation == null) - { - _result = result; - } - else if (_op == null) + if (_op == null) { _op = GetTaskForResult(result); } @@ -122,17 +117,9 @@ public void SetStateMachine(IAsyncStateMachine stateMachine) [DebuggerHidden] public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine { - // Box the stateMachine into MoveNextRunner on first await. if (_continuation == null) { - // MoveNextRunner acts as a heap-allocated shell of the state machine. - var runner = new AsyncResultMethodBuilder.MoveNextRunner(); - - // Create the continuation delegate. - _continuation = runner.Run; - - // Copy the state machine into the runner (boxing). This should be done after _continuation is initialized. - runner.SetStateMachine(ref stateMachine); + OnFirstAwait(ref stateMachine); } awaiter.OnCompleted(_continuation); @@ -148,12 +135,9 @@ public void AwaitOnCompleted(ref TAwaiter awaiter, ref [DebuggerHidden] public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine { - // Box the stateMachine into MoveNextRunner on first await. See AwaitOnCompleted for detailed comments. if (_continuation == null) { - var runner = new AsyncResultMethodBuilder.MoveNextRunner(); - _continuation = runner.Run; - runner.SetStateMachine(ref stateMachine); + OnFirstAwait(ref stateMachine); } awaiter.UnsafeOnCompleted(_continuation); @@ -169,10 +153,61 @@ public static AsyncResultMethodBuilder Create() return default(AsyncResultMethodBuilder); } + #endregion + + #region implementation + + private class MoveNextRunner : AsyncResult where TStateMachine : IAsyncStateMachine + { + private TStateMachine _stateMachine; + + public MoveNextRunner() + : base(AsyncOperationStatus.Running) + { + } + + public void SetStateMachine(ref TStateMachine stateMachine) + { + _stateMachine = stateMachine; + } + + [DebuggerHidden] + public void Run() + { + Debug.Assert(!IsCompleted); + _stateMachine.MoveNext(); + } + } + + /// + /// First await handler. Boxes the and initializes continuation action. + /// + /// Type of the state machine instance. + /// The parent state machine. + private void OnFirstAwait(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine + { + // MoveNextRunner acts as a heap-allocated shell of the state machine + task. + var runner = new MoveNextRunner(); + + // Init all members references so they are shared between this instance and the boxed one. + _continuation = runner.Run; + _op = runner; + + // Copy the state machine into the runner (boxing). This should be done after _continuation and _op is initialized. + runner.SetStateMachine(ref stateMachine); + } + + /// + /// Gets a task matching the result value specified. + /// + /// The result value. + /// The completed task. private AsyncResult GetTaskForResult(TResult result) { return AsyncResult.FromResult(result); } + + #endregion } #endif From ca031cc65b63041ecc52f04a7a557d8e5c2bbe75 Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Thu, 11 Oct 2018 16:51:31 +0300 Subject: [PATCH 15/38] CHANGELOG update --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eba06b5..44de129 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/); this proj ----------------------- ## [0.9.8] - unreleased +### Added +- `AsyncResult` is now Task-like type and can be used as `async` methd result value (requires C# 7.2). + ### Fixed - Fixed `AsyncResult` completion callbacks to be called event if `OnCompleted` throws. From 09aa38431cac198059c98ea9695f12ecfb8ef569 Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Sat, 13 Oct 2018 21:33:30 +0300 Subject: [PATCH 16/38] Fixed Unity code for C# 4 --- .../UnityFx.Async/Scripts/UnityExtensions.cs | 24 ++++++++++++++++--- .../UnityFx.Async.AssetStore.csproj | 2 +- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/UnityFx.Async.AssetStore/Assets/Plugins/UnityFx.Async/Scripts/UnityExtensions.cs b/src/UnityFx.Async.AssetStore/Assets/Plugins/UnityFx.Async/Scripts/UnityExtensions.cs index 0990e47..fc8eddb 100644 --- a/src/UnityFx.Async.AssetStore/Assets/Plugins/UnityFx.Async/Scripts/UnityExtensions.cs +++ b/src/UnityFx.Async.AssetStore/Assets/Plugins/UnityFx.Async/Scripts/UnityExtensions.cs @@ -120,7 +120,13 @@ public AsyncOperationAwaiter(AsyncOperation op) /// Gets a value indicating whether the underlying operation is completed. /// /// The operation completion flag. - public bool IsCompleted => _op.isDone; + public bool IsCompleted + { + get + { + return _op.isDone; + } + } /// /// Returns the source result value. @@ -216,7 +222,13 @@ public UnityWebRequestAwaiter(UnityWebRequest op) /// Gets a value indicating whether the underlying operation is completed. /// /// The operation completion flag. - public bool IsCompleted => _op.isDone; + public bool IsCompleted + { + get + { + return _op.isDone; + } + } /// /// Returns the source result value. @@ -314,7 +326,13 @@ public WwwAwaiter(WWW op) /// Gets a value indicating whether the underlying operation is completed. /// /// The operation completion flag. - public bool IsCompleted => _op.isDone; + public bool IsCompleted + { + get + { + return _op.isDone; + } + } /// /// Returns the source result value. diff --git a/src/UnityFx.Async.AssetStore/UnityFx.Async.AssetStore.csproj b/src/UnityFx.Async.AssetStore/UnityFx.Async.AssetStore.csproj index 43552dc..1bbd0bc 100644 --- a/src/UnityFx.Async.AssetStore/UnityFx.Async.AssetStore.csproj +++ b/src/UnityFx.Async.AssetStore/UnityFx.Async.AssetStore.csproj @@ -17,7 +17,7 @@ false true ../CodingConventions/Cs/CsharpRules.ruleset - 6 + 4 From e42895a1aeae5bb2e5ab38142803286b60755d67 Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Thu, 18 Oct 2018 12:52:40 +0300 Subject: [PATCH 17/38] Added new FromAction overload --- src/UnityFx.Async/Api/AsyncResult.Helpers.cs | 27 ++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/UnityFx.Async/Api/AsyncResult.Helpers.cs b/src/UnityFx.Async/Api/AsyncResult.Helpers.cs index 9c5078f..1ab69db 100644 --- a/src/UnityFx.Async/Api/AsyncResult.Helpers.cs +++ b/src/UnityFx.Async/Api/AsyncResult.Helpers.cs @@ -444,6 +444,33 @@ public static AsyncResult FromAction(SendOrPostCallback callback, object state) } } + /// + /// Creates a completed that represents result of the specified. + /// + /// The delegate to execute. + /// Arguments of the . + /// Thrown if is . + /// A completed operation that represents the result. + /// + /// + public static AsyncResult FromAction(Delegate callback, object[] args) + { + if (callback == null) + { + throw new ArgumentNullException(nameof(callback)); + } + + try + { + var result = callback.DynamicInvoke(args); + return new AsyncResult(result, null); + } + catch (Exception e) + { + return new AsyncResult(e, null); + } + } + /// /// Creates a completed that represents result of the specified. /// From 408dbfc489967b8989f98dc290ef08f1209fa955 Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Thu, 18 Oct 2018 12:55:44 +0300 Subject: [PATCH 18/38] Fixed AsyncState inconsistency for FromAction results --- src/UnityFx.Async/Api/AsyncResult.Helpers.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/UnityFx.Async/Api/AsyncResult.Helpers.cs b/src/UnityFx.Async/Api/AsyncResult.Helpers.cs index 1ab69db..e2c4373 100644 --- a/src/UnityFx.Async/Api/AsyncResult.Helpers.cs +++ b/src/UnityFx.Async/Api/AsyncResult.Helpers.cs @@ -412,7 +412,7 @@ public static AsyncResult FromAction(Action action, T state) } catch (Exception e) { - return new AsyncResult(e, state); + return new AsyncResult(e, null); } } @@ -440,7 +440,7 @@ public static AsyncResult FromAction(SendOrPostCallback callback, object state) } catch (Exception e) { - return new AsyncResult(e, state); + return new AsyncResult(e, null); } } @@ -516,11 +516,11 @@ public static AsyncResult FromAction(Func actio try { var result = action(state); - return new AsyncResult(result, state); + return new AsyncResult(result, null); } catch (Exception e) { - return new AsyncResult(e, state); + return new AsyncResult(e, null); } } From c8fe6d37398dc8799390368886062dc1d35e4011 Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Wed, 24 Oct 2018 14:21:49 +0300 Subject: [PATCH 19/38] README update --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a604625..cdd2a28 100644 --- a/README.md +++ b/README.md @@ -31,11 +31,13 @@ The table below summarizes differences berween *UnityFx.Async* and other popular | .NET 3.5 compilance | ✔️ | ✔️ | -️️ | | Supports continuations | ✔️ | ✔️ | ✔️ | | Supports Unity coroutines | ️️✔️ | - | - | -| Supports `async` / `await` | ✔️ | - | ✔️ | -| Supports `promise`-like continuations | ✔️ | ✔️ | - | +| Supports [async / await](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/index) | ✔️ | - | ✔️ | +| Supports [promise](https://www.promisejs.org/)-like continuations | ✔️ | ✔️ | - | | Supports cancellation | ✔️ | -️ | ✔️ | | Supports progress reporting | ✔️ | ✔️ | ✔️ | | Supports child operations | - | - | ✔️ | +| Supports [Task-like types](https://github.com/dotnet/roslyn/blob/master/docs/features/task-types.md) (requires C# 7.2) | ✔️ | - | ✔️ | +| Supports [ExecutionContext](https://docs.microsoft.com/en-us/dotnet/api/system.threading.executioncontext) flow | - | - | ✔️ | | Minimum operation data size for 32-bit systems (in bytes) | 32+ | 36+ | 40+ | | Minimum number of allocations per continuation | ~1 | 5+ | 2+ | From e1a7553406c84701d94996dade81642a11c04671 Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Mon, 29 Oct 2018 18:01:48 +0200 Subject: [PATCH 20/38] Updated test-related NuGet packages to latest versions --- src/UnityFx.Async.Tests/UnityFx.Async.Tests.csproj | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/UnityFx.Async.Tests/UnityFx.Async.Tests.csproj b/src/UnityFx.Async.Tests/UnityFx.Async.Tests.csproj index 439a6f3..9e1b61a 100644 --- a/src/UnityFx.Async.Tests/UnityFx.Async.Tests.csproj +++ b/src/UnityFx.Async.Tests/UnityFx.Async.Tests.csproj @@ -7,10 +7,13 @@ - + - - + + + all + runtime; build; native; contentfiles; analyzers + From fc96058dd394d91ea8c9a9bea7cd3183accf8f05 Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Mon, 29 Oct 2018 18:08:59 +0200 Subject: [PATCH 21/38] README update --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index cdd2a28..bcdcf08 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ Unity Asset Store | [![Asynchronous operations for Unity](https://img.shields.io **If you enjoy using the library - please, [rate and review](https://assetstore.unity.com/packages/tools/asynchronous-operations-for-unity-96696) it on the Asset Store!** +**Please ask any questions and leave feedback at the [Unity forums](https://forum.unity.com/threads/asynchronous-operations-for-unity-free.522989/).** + ## Synopsis *UnityFx.Async* introduces effective and portable asynchronous operations that can be used very much like [Tasks](https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task) in .NET or [Promises](https://developers.google.com/web/fundamentals/primers/promises) in JS. [AsyncResult](https://arvtesh.github.io/UnityFx.Async/api/netstandard2.0/UnityFx.Async.AsyncResult.html) class is an implementation of a generic asynchronous operation (aka `promise` or `future`). In many aspects it mimics [Task](https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task) (for example, it can be used with `async`/`await` operators, supports continuations and synchronization context capturing) while maintaining Unity/net35 compatibility. It is a great foundation toolset for any Unity project. From 1fedb239894db0917a522d6ade7822cbad1d447a Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Tue, 30 Oct 2018 12:37:42 +0200 Subject: [PATCH 22/38] Added more promise tests --- .../Tests/PromiseChainTests.cs | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/UnityFx.Async.Tests/Tests/PromiseChainTests.cs diff --git a/src/UnityFx.Async.Tests/Tests/PromiseChainTests.cs b/src/UnityFx.Async.Tests/Tests/PromiseChainTests.cs new file mode 100644 index 0000000..0deeddd --- /dev/null +++ b/src/UnityFx.Async.Tests/Tests/PromiseChainTests.cs @@ -0,0 +1,53 @@ +// Copyright (c) Alexander Bogarsukov. +// Licensed under the MIT license. See the LICENSE.md file in the project root for more information. + +using System; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace UnityFx.Async.Promises +{ + public class PromiseChainTests + { + [Fact] + public async Task Catch_CalledIfThenThrows() + { + // Arrange + var op = AsyncResult.Delay(10); + var thenOp = op.Then(() => throw new Exception()); + var catchCalled = false; + var catchOp = thenOp.Catch(e => catchCalled = true); + + // Act + await catchOp; + + // Assert + Assert.True(thenOp.IsFaulted); + Assert.True(catchOp.IsCompletedSuccessfully); + Assert.True(catchCalled); + } + + [Fact] + public async Task Then_NotCalledIfPreviousThenThrows() + { + // Arrange + var op = AsyncResult.Delay(10); + var thenOp = op.Then(() => throw new Exception()); + var thenCalled = false; + var thenOp2 = thenOp.Then(() => thenCalled = true); + var catchCalled = false; + var catchOp = thenOp2.Catch(e => catchCalled = true); + + // Act + await catchOp; + + // Assert + Assert.True(thenOp.IsFaulted); + Assert.True(thenOp2.IsFaulted); + Assert.True(catchOp.IsCompletedSuccessfully); + Assert.True(catchCalled); + Assert.False(thenCalled); + } + } +} From 7420a3bd8cb2b52b9e91a7385f8f07be68022f84 Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Tue, 30 Oct 2018 13:01:00 +0200 Subject: [PATCH 23/38] Fixed exception not being set correctly for AsyncResult.FaultedOperation & AsyncResult.CanceledOperation --- .../Tests/PromiseChainTests.cs | 18 ++++++++++++++++++ src/UnityFx.Async/Api/AsyncResult.cs | 12 +++++++----- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/UnityFx.Async.Tests/Tests/PromiseChainTests.cs b/src/UnityFx.Async.Tests/Tests/PromiseChainTests.cs index 0deeddd..0684795 100644 --- a/src/UnityFx.Async.Tests/Tests/PromiseChainTests.cs +++ b/src/UnityFx.Async.Tests/Tests/PromiseChainTests.cs @@ -28,6 +28,24 @@ public async Task Catch_CalledIfThenThrows() Assert.True(catchCalled); } + [Fact] + public async Task Catch_CalledIfThenFails() + { + // Arrange + var op = AsyncResult.Delay(10); + var thenOp = op.Then(() => AsyncResult.FaultedOperation); + var catchCalled = false; + var catchOp = thenOp.Catch(e => catchCalled = true); + + // Act + await catchOp; + + // Assert + Assert.True(thenOp.IsFaulted); + Assert.True(catchOp.IsCompletedSuccessfully); + Assert.True(catchCalled); + } + [Fact] public async Task Then_NotCalledIfPreviousThenThrows() { diff --git a/src/UnityFx.Async/Api/AsyncResult.cs b/src/UnityFx.Async/Api/AsyncResult.cs index 89dd2f1..9fd0fd5 100644 --- a/src/UnityFx.Async/Api/AsyncResult.cs +++ b/src/UnityFx.Async/Api/AsyncResult.cs @@ -1309,9 +1309,9 @@ private string DebuggerDisplay { result += " (" + ((int)(GetProgress() * 100)).ToString(CultureInfo.InvariantCulture) + "%)"; } - else if ((status == AsyncOperationStatus.Faulted || status == AsyncOperationStatus.Canceled) && _exception != null) + else if (status == AsyncOperationStatus.Faulted || status == AsyncOperationStatus.Canceled) { - result += " (" + _exception.GetType().Name + ')'; + result += " (" + (_exception != null ? _exception.GetType().Name : "null") + ')'; } if (IsDisposed) @@ -1325,16 +1325,18 @@ private string DebuggerDisplay private AsyncResult(int flags) { - if (flags == StatusFaulted) + var status = flags & _statusMask; + + if (status == StatusFaulted) { _exception = new Exception(); } - else if (flags == StatusCanceled) + else if (status == StatusCanceled) { _exception = new OperationCanceledException(); } - if ((flags & _statusMask) > StatusRunning) + if (status > StatusRunning) { _callback = _callbackCompletionSentinel; _flags = flags | _flagCompletedSynchronously; From b2afd1e3ddac865568ab0b24bb749a7988230727 Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Tue, 30 Oct 2018 15:20:31 +0200 Subject: [PATCH 24/38] Disabled MovieTexture usage for Android and iOS targets (it is not supported on mobiles). This fixes #3. --- .../Plugins/UnityFx.Async/Scripts/AsyncWww.cs | 21 +++++++++------- .../Tests/PromiseChainTests.cs | 24 +++++++++++++++++++ 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/UnityFx.Async.AssetStore/Assets/Plugins/UnityFx.Async/Scripts/AsyncWww.cs b/src/UnityFx.Async.AssetStore/Assets/Plugins/UnityFx.Async/Scripts/AsyncWww.cs index fdf60c6..d7e7062 100644 --- a/src/UnityFx.Async.AssetStore/Assets/Plugins/UnityFx.Async/Scripts/AsyncWww.cs +++ b/src/UnityFx.Async.AssetStore/Assets/Plugins/UnityFx.Async/Scripts/AsyncWww.cs @@ -204,7 +204,7 @@ public static IAsyncOperation GetTexture(string url, bool nonReadable return result; } -#if !UNITY_2018_2_OR_NEWER +#if !UNITY_2018_2_OR_NEWER && !UNITY_IOS && !UNITY_ANDROID /// /// Creates an asyncronous operation optimized for downloading a via HTTP GET. @@ -254,7 +254,7 @@ public static T GetResult(UnityWebRequest request) where T : class { return ((DownloadHandlerAudioClip)request.downloadHandler).audioClip as T; } -#if !UNITY_5 && !UNITY_2018_2_OR_NEWER +#if !UNITY_5 && !UNITY_2018_2_OR_NEWER && !UNITY_IOS && !UNITY_ANDROID else if (request.downloadHandler is DownloadHandlerMovieTexture) { return ((DownloadHandlerMovieTexture)request.downloadHandler).movieTexture as T; @@ -298,21 +298,24 @@ public static T GetResult(WWW request) where T : class { return request.GetAudioClip() as T; } -#if !UNITY_2018_2_OR_NEWER - else if (typeof(T) == typeof(MovieTexture)) - { - return request.GetMovieTexture() as T; - } -#endif #else else if (typeof(T) == typeof(AudioClip)) { return request.audioClip as T; } +#endif +#if !UNITY_2018_2_OR_NEWER && !UNITY_IOS && !UNITY_ANDROID +#if UNITY_5_6_OR_NEWER + else if (typeof(T) == typeof(MovieTexture)) + { + return request.GetMovieTexture() as T; + } +#else else if (typeof(T) == typeof(MovieTexture)) { return request.movie as T; } +#endif #endif else if (typeof(T) == typeof(byte[])) { @@ -327,5 +330,5 @@ public static T GetResult(WWW request) where T : class } #endif + } } -} diff --git a/src/UnityFx.Async.Tests/Tests/PromiseChainTests.cs b/src/UnityFx.Async.Tests/Tests/PromiseChainTests.cs index 0684795..94f7987 100644 --- a/src/UnityFx.Async.Tests/Tests/PromiseChainTests.cs +++ b/src/UnityFx.Async.Tests/Tests/PromiseChainTests.cs @@ -24,6 +24,7 @@ public async Task Catch_CalledIfThenThrows() // Assert Assert.True(thenOp.IsFaulted); + Assert.IsType(thenOp.Exception); Assert.True(catchOp.IsCompletedSuccessfully); Assert.True(catchCalled); } @@ -42,6 +43,7 @@ public async Task Catch_CalledIfThenFails() // Assert Assert.True(thenOp.IsFaulted); + Assert.IsType(thenOp.Exception); Assert.True(catchOp.IsCompletedSuccessfully); Assert.True(catchCalled); } @@ -67,5 +69,27 @@ public async Task Then_NotCalledIfPreviousThenThrows() Assert.True(catchCalled); Assert.False(thenCalled); } + + [Fact] + public async Task Then_NotCalledIfPreviousThenFails() + { + // Arrange + var op = AsyncResult.Delay(10); + var thenOp = op.Then(() => AsyncResult.FaultedOperation); + var thenCalled = false; + var thenOp2 = thenOp.Then(() => thenCalled = true); + var catchCalled = false; + var catchOp = thenOp2.Catch(e => catchCalled = true); + + // Act + await catchOp; + + // Assert + Assert.True(thenOp.IsFaulted); + Assert.True(thenOp2.IsFaulted); + Assert.True(catchOp.IsCompletedSuccessfully); + Assert.True(catchCalled); + Assert.False(thenCalled); + } } } From 310369144142b4022defd697be841b88f299ca13 Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Thu, 1 Nov 2018 11:57:10 +0200 Subject: [PATCH 25/38] Removed AsyncResultQueue --- .../Tests/AsyncResultQueueTests.cs | 73 --- .../Api/Helpers/AsyncResultQueue{T}.cs | 431 ------------------ 2 files changed, 504 deletions(-) delete mode 100644 src/UnityFx.Async.Tests/Tests/AsyncResultQueueTests.cs delete mode 100644 src/UnityFx.Async/Api/Helpers/AsyncResultQueue{T}.cs diff --git a/src/UnityFx.Async.Tests/Tests/AsyncResultQueueTests.cs b/src/UnityFx.Async.Tests/Tests/AsyncResultQueueTests.cs deleted file mode 100644 index 9c2acf8..0000000 --- a/src/UnityFx.Async.Tests/Tests/AsyncResultQueueTests.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) Alexander Bogarsukov. -// Licensed under the MIT license. See the LICENSE.md file in the project root for more information. - -using System; -using System.Threading; -using System.Threading.Tasks; -using Xunit; - -namespace UnityFx.Async -{ - public class AsyncResultQueueTests - { - [Fact] - public void DefaultConstructor_InitializesEmptyQueue() - { - // Arrange/Act - var queue = new AsyncResultQueue(); - - // Assert - Assert.True(queue.IsEmpty); - Assert.False(queue.Suspended); - Assert.Empty(queue); - Assert.Equal(0, queue.MaxCount); - Assert.Null(queue.Current); - } - - [Fact] - public void Add_AddsNewOperation() - { - // Arrage - var queue = new AsyncResultQueue(); - var op = new AsyncResult(); - - // Act - var result = queue.Add(op); - - // Assert - Assert.True(result); - Assert.False(queue.IsEmpty); - Assert.NotEmpty(queue); - Assert.Equal(op, queue.Current); - Assert.Equal(AsyncOperationStatus.Running, op.Status); - } - - [Fact] - public void Add_ThrowsOnCompletedOperations() - { - // Arrage - var queue = new AsyncResultQueue(); - var op = AsyncResult.CompletedOperation; - - // Act/Assert - Assert.Throws(() => queue.Add(op)); - } - - [Fact] - public void Clear_RemovesAllOperations() - { - // Arrage - var op = new AsyncResult(); - var op2 = new AsyncResult(); - var queue = new AsyncResultQueue() { op, op2 }; - - // Act - queue.Clear(); - - // Assert - Assert.True(queue.IsEmpty); - Assert.Empty(queue); - Assert.Null(queue.Current); - } - } -} diff --git a/src/UnityFx.Async/Api/Helpers/AsyncResultQueue{T}.cs b/src/UnityFx.Async/Api/Helpers/AsyncResultQueue{T}.cs deleted file mode 100644 index 3d6d577..0000000 --- a/src/UnityFx.Async/Api/Helpers/AsyncResultQueue{T}.cs +++ /dev/null @@ -1,431 +0,0 @@ -// Copyright (c) Alexander Bogarsukov. -// Licensed under the MIT license. See the LICENSE.md file in the project root for more information. - -using System; -using System.Collections; -#if !NET35 -using System.Collections.Concurrent; -#endif -using System.Collections.Generic; -using System.Threading; - -namespace UnityFx.Async -{ - /// - /// A FIFO queue of instances that are executed sequentially. - /// - /// - /// Completed operations are removed from the queue automatically. All interaction with queued operations is done - /// through the that can be specified on queue construction. - /// - /// - /// - [Obsolete] -#if NET35 - public class AsyncResultQueue : IEnumerable, IAsyncCancellable where T : AsyncResult -#else - public class AsyncResultQueue : IReadOnlyCollection, IAsyncCancellable where T : AsyncResult -#endif - { - #region data - - private readonly SynchronizationContext _syncContext; - - private int _maxOpsSize = 0; - private bool _suspended; -#if NET35 - private List _ops = new List(); -#else - private ConcurrentQueue _ops = new ConcurrentQueue(); -#endif - private SendOrPostCallback _startCallback; - private Action _completionCallback; - - #endregion - - #region interface - - /// - /// Gets a value indicating whether the queue is empty. - /// - /// The empty flag. - /// - public bool IsEmpty - { - get - { -#if NET35 - return _ops.Count == 0; -#else - return _ops.IsEmpty; -#endif - } - } - - /// - /// Gets or sets maximum queue size. Default is 0 (no constraints). - /// - /// Maximum queue size. Zero means no constraints. - /// Thrown if the value is less than 0. - /// - public int MaxCount - { - get - { - return _maxOpsSize; - } - set - { - if (value < 0) - { - throw new ArgumentOutOfRangeException(nameof(MaxCount), value, Messages.FormatError_ValueIsLessThanZero()); - } - - _maxOpsSize = value; - } - } - - /// - /// Gets or sets a value indicating whether the queue in on pause. When in suspended state queue does not change state of the operations. - /// - /// The paused flag. - public bool Suspended - { - get - { - return _suspended; - } - set - { - _suspended = value; - - if (!_suspended) - { - TryStart(null); - } - } - } - - /// - /// Gets an operation that is running currently or if the queue is empty. - /// - /// An operation that is running currently. - public T Current - { - get - { -#if NET35 - lock (_ops) - { - return _ops.Count > 0 ? _ops[0] : null; - } -#else - return _ops.TryPeek(out var result) ? result : null; -#endif - } - } - - /// - /// Initializes a new instance of the class. - /// - public AsyncResultQueue() - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The synchronization context to to use for marshaling operation calls to specific thread. Can have value. - public AsyncResultQueue(SynchronizationContext syncContext) - { - _syncContext = syncContext; - } - - /// - /// Adds a new operation to the end of the queue. Operation is expected to have its status set to . - /// - /// The operation to enqueue. - /// Thrown if is . - /// Thrown if the operatinon is started. - /// - public bool Add(T op) - { - if (op == null) - { - throw new ArgumentNullException(nameof(op)); - } - - if (op.IsStarted) - { - throw new InvalidOperationException(); - } - - if (_maxOpsSize > 0 && _ops.Count >= _maxOpsSize) - { - return false; - } - - if (_completionCallback == null) - { - _completionCallback = OnCompletedCallback; - } - - op.AddCompletionCallback(_completionCallback, _syncContext); - -#if NET35 - lock (_ops) - { - _ops.Add(op); - TryStart(op); - } -#else - _ops.Enqueue(op); - TryStart(op); -#endif - - return true; - } - - /// - /// Removes all elements from the collection. - /// - public void Clear() - { -#if NET35 - lock (_ops) - { - foreach (var op in _ops) - { - op.RemoveCompletionCallback(_completionCallback); - } - - _ops.Clear(); - } -#else - while (_ops.TryDequeue(out var op)) - { - op.RemoveCompletionCallback(_completionCallback); - } -#endif - } - - /// - /// Copies the collection elements to an array. - /// - public void CopyTo(T[] array, int arrayIndex) - { -#if NET35 - lock (_ops) - { - _ops.CopyTo(array, arrayIndex); - } -#else - _ops.CopyTo(array, arrayIndex); -#endif - } - - /// - /// Returns the queue snapshot as array. - /// - /// An array containing the queue snapshot. - public T[] ToArray() - { -#if NET35 - lock (_ops) - { - return _ops.ToArray(); - } -#else - return _ops.ToArray(); -#endif - } - - #endregion - - #region IAsyncCancellable - - /// - public void Cancel() - { -#if NET35 - lock (_ops) - { - foreach (var op in _ops) - { - op.RemoveCompletionCallback(_completionCallback); - op.Cancel(); - } - - _ops.Clear(); - } -#else - while (_ops.TryDequeue(out var op)) - { - op.RemoveCompletionCallback(_completionCallback); - op.Cancel(); - } -#endif - } - - #endregion - - #region IReadOnlyCollection - - /// - public int Count => _ops.Count; - - #endregion - - #region IEnumerable - -#if NET35 - - private class Enumerator : IEnumerator - { - private List _list; - private List.Enumerator _enumerator; - - public Enumerator(List list) - { - Monitor.Enter(list); - - _list = list; - _enumerator = list.GetEnumerator(); - } - - public T Current => _enumerator.Current; - - object IEnumerator.Current => _enumerator.Current; - - public bool MoveNext() => _enumerator.MoveNext(); - - public void Reset() => throw new NotSupportedException(); - - public void Dispose() - { - if (_list != null) - { - _enumerator.Dispose(); - Monitor.Exit(_list); - _list = null; - } - } - } - - /// - public IEnumerator GetEnumerator() - { - return new Enumerator(_ops); - } - - /// - IEnumerator IEnumerable.GetEnumerator() - { - return new Enumerator(_ops); - } - -#else - - /// - public IEnumerator GetEnumerator() - { - return _ops.GetEnumerator(); - } - - /// - IEnumerator IEnumerable.GetEnumerator() - { - return _ops.GetEnumerator(); - } - -#endif - - #endregion - - #region implementation - - private void TryStart(T op) - { - if (!_suspended) - { - if (_syncContext == null || _syncContext == SynchronizationContext.Current) - { - TryStartUnsafe(op); - } - else - { - if (_startCallback == null) - { - _startCallback = OnStartCallback; - } - - _syncContext.Post(_startCallback, op); - } - } - } - - private void TryStartUnsafe(T op) - { - if (!_suspended) - { - op?.TrySetScheduled(); - -#if NET35 - while (_ops.Count > 0) - { - var firstOp = _ops[0]; - - if (firstOp.IsCompleted) - { - _ops.RemoveAt(0); - } - else - { - firstOp.TrySetRunning(); - break; - } - } -#else - while (_ops.TryPeek(out var firstOp)) - { - if (firstOp.IsCompleted) - { - _ops.TryDequeue(out firstOp); - } - else - { - firstOp.TrySetRunning(); - break; - } - } -#endif - } - } - - private void OnStartCallback(object args) - { -#if NET35 - lock (_ops) - { - TryStartUnsafe(args as T); - } -#else - TryStartUnsafe(args as T); -#endif - } - - private void OnCompletedCallback(IAsyncOperation op) - { -#if NET35 - lock (_ops) - { - TryStartUnsafe(null); - } -#else - TryStartUnsafe(null); -#endif - } - - #endregion - } -} From 8405a4faf44802059427e153ae8a47d3adf021fe Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Thu, 1 Nov 2018 12:01:38 +0200 Subject: [PATCH 26/38] CHANGELOG update --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44de129..7167008 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,12 +8,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/); this proj ### Added - `AsyncResult` is now Task-like type and can be used as `async` methd result value (requires C# 7.2). +- Added new `AsyncResult.FromAction` overloads. ### Fixed - Fixed `AsyncResult` completion callbacks to be called event if `OnCompleted` throws. +- Fixed exception not been set for `AsyncResult.FaultedOperation` and `AsyncResult.CanceledOperation`. +- Disabled `MovieTexture` helpers for iOS/Android (as it is not supported on mobiles). ### Removed -- Deprecated `AsyncResultQueue`. +- Removed `AsyncResultQueue`. ----------------------- ## [0.9.7] - 2018.09.27 From 3652f67f658455f0642d801175dac1193d79d42e Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Thu, 1 Nov 2018 12:05:22 +0200 Subject: [PATCH 27/38] README update --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bcdcf08..039ccfa 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Channel | UnityFx.Async | AppVeyor | [![Build status](https://ci.appveyor.com/api/projects/status/hfmq9vow53al7tpd/branch/master?svg=true)](https://ci.appveyor.com/project/Arvtesh/unityfx-async/branch/master) [![AppVeyor tests](https://img.shields.io/appveyor/tests/Arvtesh/unityFx-async.svg)](https://ci.appveyor.com/project/Arvtesh/unityfx-async/build/tests) NuGet | [![NuGet](https://img.shields.io/nuget/v/UnityFx.Async.svg)](https://www.nuget.org/packages/UnityFx.Async) Github | [![GitHub release](https://img.shields.io/github/release/Arvtesh/UnityFx.Async.svg?logo=github)](https://github.com/Arvtesh/UnityFx.Async/releases) -Unity Asset Store | [![Asynchronous operations for Unity](https://img.shields.io/badge/tools-v0.9.7-green.svg)](https://assetstore.unity.com/packages/tools/asynchronous-operations-for-unity-96696) +Unity Asset Store | [![Asynchronous operations for Unity](https://img.shields.io/badge/tools-v0.9.8-green.svg)](https://assetstore.unity.com/packages/tools/asynchronous-operations-for-unity-96696) **Requires Unity 5.4 or higher.** @@ -29,8 +29,8 @@ The table below summarizes differences berween *UnityFx.Async* and other popular | Stat | UnityFx.Async | [C-Sharp-Promise](https://github.com/Real-Serious-Games/C-Sharp-Promise) | [TPL](https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/task-parallel-library-tpl) | | :--- | :---: | :---: | :---: | | Thread-safe | ✔️ | - | ✔️ | -| Can capture synchronization context | ✔️ | - | ✔️ | | .NET 3.5 compilance | ✔️ | ✔️ | -️️ | +| Supports [SynchronizationContext](https://docs.microsoft.com/en-us/dotnet/api/system.threading.synchronizationcontext) capturing | ✔️ | - | ✔️ | | Supports continuations | ✔️ | ✔️ | ✔️ | | Supports Unity coroutines | ️️✔️ | - | - | | Supports [async / await](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/index) | ✔️ | - | ✔️ | @@ -519,7 +519,6 @@ TPL | UnityFx.Async | Notes - | [IAsyncCancellable](https://arvtesh.github.io/UnityFx.Async/api/netstandard2.0/UnityFx.Async.IAsyncCancellable.html) | A cancellable operation. - | [IAsyncContinuation](https://arvtesh.github.io/UnityFx.Async/api/netstandard2.0/UnityFx.Async.IAsyncContinuation.html) | A generic non-delegate operation continuation. - | [IAsyncUpdatable](https://arvtesh.github.io/UnityFx.Async/api/netstandard2.0/UnityFx.Async.IAsyncUpdatable.html), [IAsyncUpdateSource](https://arvtesh.github.io/UnityFx.Async/api/netstandard2.0/UnityFx.Async.IAsyncUpdateSource.html) | A consumer and provider sides for frame update notifications. -- | [AsyncResultQueue<T>](https://arvtesh.github.io/UnityFx.Async/api/netstandard2.0/UnityFx.Async.AsyncResultQueue-1.html) | A FIFO queue of asynchronous operations executed sequentially. Please note that the library is NOT a replacement for [Tasks](https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task) or [TPL](https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/task-parallel-library-tpl). As a general rule it is recommended to use [Tasks](https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task) and only switch to *UnityFx.Async* if one of the following applies: - .NET 3.5/[Unity3d](https://unity3d.com) compatibility is required. From 657e5e30407e8838822d7616236fe41cb669293f Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Thu, 1 Nov 2018 18:19:17 +0200 Subject: [PATCH 28/38] Removed AsyncLazy --- .../Api/{Helpers => }/AsyncUpdateSource.cs | 0 src/UnityFx.Async/Api/Helpers/AsyncLazy.cs | 123 ------------------ 2 files changed, 123 deletions(-) rename src/UnityFx.Async/Api/{Helpers => }/AsyncUpdateSource.cs (100%) delete mode 100644 src/UnityFx.Async/Api/Helpers/AsyncLazy.cs diff --git a/src/UnityFx.Async/Api/Helpers/AsyncUpdateSource.cs b/src/UnityFx.Async/Api/AsyncUpdateSource.cs similarity index 100% rename from src/UnityFx.Async/Api/Helpers/AsyncUpdateSource.cs rename to src/UnityFx.Async/Api/AsyncUpdateSource.cs diff --git a/src/UnityFx.Async/Api/Helpers/AsyncLazy.cs b/src/UnityFx.Async/Api/Helpers/AsyncLazy.cs deleted file mode 100644 index 91ceef2..0000000 --- a/src/UnityFx.Async/Api/Helpers/AsyncLazy.cs +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (c) Alexander Bogarsukov. -// Licensed under the MIT license. See the LICENSE.md file in the project root for more information. - -using System; - -namespace UnityFx.Async -{ - /// - /// A helper for lazy initialization. - /// - /// - /// This value-type is mutable, so DO NOT create readonly instances of it. - /// - /// - public struct AsyncLazy - { - #region data - - private Func _opFactory; - private IAsyncOperation _op; - - #endregion - - #region interface - - /// - /// Gets a value indicating whether the operation has started. - /// - public bool IsStarted => _op != null; - - /// - /// Gets a value indicating whether the operation completed successfully (i.e. with status). - /// - public bool IsCompletedSuccessfully => _op?.IsCompletedSuccessfully ?? false; - - /// - /// Gets a value indicating whether the operation has completed. - /// - public bool IsCompleted => _op?.IsCompleted ?? false; - - /// - /// Gets a value indicating whether the operation completed due to an unhandled exception (i.e. with status). - /// - public bool IsFaulted => _op?.IsFaulted ?? false; - - /// - /// Gets a value indicating whether the operation completed due to being canceled (i.e. with status). - /// - public bool IsCanceled => _op?.IsCanceled ?? false; - - /// - /// Initializes a new instance of the struct. - /// - /// The delegate that is invoked to produce the lazily initialized operation when it is needed. - /// Thrown if value is . - public AsyncLazy(Func opFactory) - { - _opFactory = opFactory ?? throw new ArgumentNullException(nameof(opFactory)); - _op = null; - } - - /// - /// Starts the operation or just updates its state. - /// - /// Thrown if operation factory is . - public IAsyncOperation StartOrUpdate() - { - if (_op != null) - { - UpdateOperation(); - } - else - { - _op = _opFactory?.Invoke() ?? throw new InvalidOperationException(); - } - - return _op; - } - - /// - /// Resets state of the instance to default. - /// - public void Reset() - { - _op = null; - } - -#if !NET35 - - /// - /// Returns the operation awaiter. This method is intended for compiler use only. - /// - public CompilerServices.AsyncAwaiter GetAwaiter() - { - var op = StartOrUpdate(); - return new CompilerServices.AsyncAwaiter(op); - } - -#endif - - #endregion - - #region implementation - - private void UpdateOperation() - { - if (_opFactory != null) - { - var status = _op.Status; - - switch (status) - { - case AsyncOperationStatus.RanToCompletion: - _op = AsyncResult.CompletedOperation; - _opFactory = null; - break; - } - } - } - - #endregion - } -} From 752ee22ab61720896eb01ab932f283e8109bd66a Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Thu, 1 Nov 2018 18:19:52 +0200 Subject: [PATCH 29/38] CHANGELOG update --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7167008..dd4d72f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/); this proj ### Removed - Removed `AsyncResultQueue`. +- Removed `AsyncLazy`. ----------------------- ## [0.9.7] - 2018.09.27 From 0123457a18d2d22b1c2f9b311120b3875340f06b Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Thu, 1 Nov 2018 19:40:11 +0200 Subject: [PATCH 30/38] Added SynchronizationContext extensions (async versions of Send/Post) --- .../Api/Extensions/AsyncExtensions.cs | 163 ++++++++++++++++++ .../Specialized/ActionResult.cs | 62 +++++++ .../Specialized/ActionResult{T}.cs | 44 +++++ 3 files changed, 269 insertions(+) create mode 100644 src/UnityFx.Async/Implementation/Specialized/ActionResult.cs create mode 100644 src/UnityFx.Async/Implementation/Specialized/ActionResult{T}.cs diff --git a/src/UnityFx.Async/Api/Extensions/AsyncExtensions.cs b/src/UnityFx.Async/Api/Extensions/AsyncExtensions.cs index b23957b..d288e85 100644 --- a/src/UnityFx.Async/Api/Extensions/AsyncExtensions.cs +++ b/src/UnityFx.Async/Api/Extensions/AsyncExtensions.cs @@ -269,6 +269,32 @@ public static void Send(this SynchronizationContext context, Action action) context.Post(_actionCallback, action); } + /// + /// Dispatches an synchronous message to a synchronization context. + /// + /// The target context. + /// The delegate to invoke. + /// Thrown if is . + /// Returns result of the call. + public static T Send(this SynchronizationContext context, Func action) + { + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + + var result = default(T); + + context.Send( + state => + { + result = ((Func)state)(); + }, + action); + + return result; + } + /// /// Dispatches an asynchronous message to a synchronization context. /// @@ -290,6 +316,85 @@ public static void Post(this SynchronizationContext context, Action action) context.Post(_actionCallback, action); } + /// + /// Dispatches an asynchronous message to a synchronization context. + /// + /// The target context. + /// The delegate to invoke. + /// Thrown if is . + /// An that can be used to track the operation status. + public static IAsyncOperation PostAsync(this SynchronizationContext context, Action action) + { + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + + var op = new ActionResult(action); + + context.Post( + state => + { + ((ActionResult)state).Start(); + }, + op); + + return op; + } + + /// + /// Dispatches an asynchronous message to a synchronization context. + /// + /// The target context. + /// The delegate to invoke. + /// User-defined state. + /// Thrown if is . + /// An that can be used to track the operation status. + public static IAsyncOperation PostAsync(this SynchronizationContext context, SendOrPostCallback d, object state) + { + if (d == null) + { + throw new ArgumentNullException(nameof(d)); + } + + var op = new ActionResult(d, state); + + context.Post( + s => + { + ((ActionResult)s).Start(); + }, + op); + + return op; + } + + /// + /// Dispatches an asynchronous message to a synchronization context. + /// + /// The target context. + /// The delegate to invoke. + /// Thrown if is . + /// An that can be used to track the operation status. + public static IAsyncOperation PostAsync(this SynchronizationContext context, Func action) + { + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + + var op = new ActionResult(action); + + context.Post( + state => + { + ((ActionResult)state).Start(); + }, + op); + + return op; + } + /// /// Dispatches a message to a synchronization context. /// @@ -313,6 +418,25 @@ public static void Invoke(this SynchronizationContext context, Action action) } } + /// + /// Dispatches a message to a synchronization context. + /// + /// The target context. + /// The delegate to invoke. + /// Thrown if is . + /// An that can be used to track the operation status. + public static IAsyncOperation InvokeAsync(this SynchronizationContext context, Action action) + { + if (context == SynchronizationContext.Current) + { + return AsyncResult.FromAction(action); + } + else + { + return PostAsync(context, action); + } + } + /// /// Dispatches a message to a synchronization context. /// @@ -337,6 +461,45 @@ public static void Invoke(this SynchronizationContext context, SendOrPostCallbac } } + /// + /// Dispatches a message to a synchronization context. + /// + /// The target context. + /// The delegate to invoke. + /// User-defined state. + /// Thrown if is . + /// An that can be used to track the operation status. + public static IAsyncOperation InvokeAsync(this SynchronizationContext context, SendOrPostCallback d, object state) + { + if (context == SynchronizationContext.Current) + { + return AsyncResult.FromAction(d, state); + } + else + { + return PostAsync(context, d, state); + } + } + + /// + /// Dispatches a message to a synchronization context. + /// + /// The target context. + /// The delegate to invoke. + /// Thrown if is . + /// An that can be used to track the operation status. + public static IAsyncOperation InvokeAsync(this SynchronizationContext context, Func action) + { + if (context == SynchronizationContext.Current) + { + return AsyncResult.FromAction(action); + } + else + { + return PostAsync(context, action); + } + } + #endregion #region IAsyncOperationEvents diff --git a/src/UnityFx.Async/Implementation/Specialized/ActionResult.cs b/src/UnityFx.Async/Implementation/Specialized/ActionResult.cs new file mode 100644 index 0000000..b5e61ae --- /dev/null +++ b/src/UnityFx.Async/Implementation/Specialized/ActionResult.cs @@ -0,0 +1,62 @@ +// Copyright (c) Alexander Bogarsukov. +// Licensed under the MIT license. See the LICENSE.md file in the project root for more information. + +using System; +using System.Diagnostics; +using System.Threading; + +namespace UnityFx.Async +{ + internal sealed class ActionResult : AsyncResult + { + #region data + + private readonly object _action; + + #endregion + + #region interface + + public ActionResult(Action action) + : base(AsyncOperationStatus.Scheduled) + { + _action = action; + } + + public ActionResult(SendOrPostCallback action, object state) + : base(AsyncOperationStatus.Scheduled, state) + { + _action = action; + } + + #endregion + + #region AsyncResult + + protected override void OnStarted() + { + try + { + if (_action is Action a) + { + a(); + } + else + { + ((SendOrPostCallback)_action)(AsyncState); + } + + TrySetCompleted(); + } + catch (Exception e) + { + TrySetException(e); + } + } + + #endregion + + #region implementation + #endregion + } +} diff --git a/src/UnityFx.Async/Implementation/Specialized/ActionResult{T}.cs b/src/UnityFx.Async/Implementation/Specialized/ActionResult{T}.cs new file mode 100644 index 0000000..815ba1c --- /dev/null +++ b/src/UnityFx.Async/Implementation/Specialized/ActionResult{T}.cs @@ -0,0 +1,44 @@ +// Copyright (c) Alexander Bogarsukov. +// Licensed under the MIT license. See the LICENSE.md file in the project root for more information. + +using System; +using System.Diagnostics; +using System.Threading; + +namespace UnityFx.Async +{ + internal sealed class ActionResult : AsyncResult + { + #region data + + private readonly Func _action; + + #endregion + + #region interface + + public ActionResult(Func action) + : base(AsyncOperationStatus.Scheduled) + { + _action = action; + } + + #endregion + + #region AsyncResult + + protected override void OnStarted() + { + try + { + TrySetResult(_action()); + } + catch (Exception e) + { + TrySetException(e); + } + } + + #endregion + } +} From 14cc259a0a73ca53327ad6dbb8b7e05f63d2b01a Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Sat, 3 Nov 2018 14:08:04 +0200 Subject: [PATCH 31/38] Moved BCL extensions to namespace UnityFx.Async.Extensions --- .../UnityFx.Async/Scripts/AsyncUtility.cs | 1 + src/UnityFx.Async/Api/AsyncExtensions.cs | 1490 +++++++++++++++++ src/UnityFx.Async/Api/AsyncResult.cs | 29 +- .../AsyncExtensions.Continuations.cs | 305 ---- .../Api/Extensions/AsyncExtensions.Tasks.cs | 216 --- .../Api/Extensions/AsyncExtensions.Wait.cs | 531 ------ .../Api/Extensions/AsyncExtensions.cs | 983 ----------- .../Api/Extensions/IAsyncResultExtensions.cs | 255 +++ ...bservables.cs => IObservableExtensions.cs} | 9 +- .../SynchronizationContextExtensions.cs | 287 ++++ .../Api/Extensions/TaskExtensions.cs | 42 + 11 files changed, 2107 insertions(+), 2041 deletions(-) create mode 100644 src/UnityFx.Async/Api/AsyncExtensions.cs delete mode 100644 src/UnityFx.Async/Api/Extensions/AsyncExtensions.Continuations.cs delete mode 100644 src/UnityFx.Async/Api/Extensions/AsyncExtensions.Tasks.cs delete mode 100644 src/UnityFx.Async/Api/Extensions/AsyncExtensions.Wait.cs delete mode 100644 src/UnityFx.Async/Api/Extensions/AsyncExtensions.cs create mode 100644 src/UnityFx.Async/Api/Extensions/IAsyncResultExtensions.cs rename src/UnityFx.Async/Api/Extensions/{AsyncExtensions.Observables.cs => IObservableExtensions.cs} (74%) create mode 100644 src/UnityFx.Async/Api/Extensions/SynchronizationContextExtensions.cs create mode 100644 src/UnityFx.Async/Api/Extensions/TaskExtensions.cs diff --git a/src/UnityFx.Async.AssetStore/Assets/Plugins/UnityFx.Async/Scripts/AsyncUtility.cs b/src/UnityFx.Async.AssetStore/Assets/Plugins/UnityFx.Async/Scripts/AsyncUtility.cs index 5b5192d..875bea3 100644 --- a/src/UnityFx.Async.AssetStore/Assets/Plugins/UnityFx.Async/Scripts/AsyncUtility.cs +++ b/src/UnityFx.Async.AssetStore/Assets/Plugins/UnityFx.Async/Scripts/AsyncUtility.cs @@ -11,6 +11,7 @@ using System.Threading; using UnityEngine; using UnityEngine.Networking; +using UnityFx.Async.Extensions; namespace UnityFx.Async { diff --git a/src/UnityFx.Async/Api/AsyncExtensions.cs b/src/UnityFx.Async/Api/AsyncExtensions.cs new file mode 100644 index 0000000..0d78855 --- /dev/null +++ b/src/UnityFx.Async/Api/AsyncExtensions.cs @@ -0,0 +1,1490 @@ +// Copyright (c) Alexander Bogarsukov. +// Licensed under the MIT license. See the LICENSE.md file in the project root for more information. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Threading; +#if !NET35 +using System.Threading.Tasks; +#endif + +namespace UnityFx.Async +{ + /// + /// Extension methods for and related classes. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class AsyncExtensions + { + #region data + +#if !NET35 + + private static Action _cancelHandler; + +#endif + + #endregion + + #region IAsyncOperation + + #region Common + + /// + /// Throws if the specified operation is faulted/canceled. + /// + public static void ThrowIfNonSuccess(this IAsyncOperation op) + { + var status = op.Status; + + if (status == AsyncOperationStatus.Faulted) + { + if (!AsyncResult.TryThrowException(op.Exception)) + { + // Should never get here. Exception should never be null in faulted state. + throw new Exception(); + } + } + else if (status == AsyncOperationStatus.Canceled) + { + if (!AsyncResult.TryThrowException(op.Exception)) + { + throw new OperationCanceledException(); + } + } + } + +#if !NET35 + + /// + /// Registers a that can be used to cancel the specified operation. + /// + /// An operation to register for. + /// A cancellation token that can be used to cancel the operation. + /// Thrown if the target operation does not support cancellation. + /// Returns the target operation. + public static IAsyncOperation WithCancellation(this IAsyncOperation op, CancellationToken cancellationToken) + { + if (cancellationToken.CanBeCanceled && !op.IsCompleted) + { + if (cancellationToken.IsCancellationRequested) + { + op.Cancel(); + } + else + { + if (_cancelHandler == null) + { + _cancelHandler = args => (args as IAsyncCancellable).Cancel(); + } + + cancellationToken.Register(_cancelHandler, op, false); + } + } + + return op; + } + +#endif + + #endregion + + #region Wait + + /// + /// Waits for the to complete execution. After that rethrows the operation exception (if any). + /// + /// The operation to wait for. + /// Thrown is the operation is disposed. + /// + /// + public static void Wait(this IAsyncOperation op) + { + if (!op.IsCompleted) + { + op.AsyncWaitHandle.WaitOne(); + } + + ThrowIfNonSuccess(op); + } + + /// + /// Waits for the to complete execution within a specified number of milliseconds. After that rethrows the operation exception (if any). + /// + /// The operation to wait for. + /// The number of milliseconds to wait, or (-1) to wait indefinitely. + /// if the operation completed execution within the allotted time; otherwise, . + /// is a negative number other than -1. + /// Thrown is the operation is disposed. + /// + /// + public static bool Wait(this IAsyncOperation op, int millisecondsTimeout) + { + var result = true; + + if (!op.IsCompleted) + { + result = op.AsyncWaitHandle.WaitOne(millisecondsTimeout); + } + + if (result) + { + ThrowIfNonSuccess(op); + } + + return result; + } + + /// + /// Waits for the to complete execution within a specified time interval. After that rethrows the operation exception (if any). + /// + /// The operation to wait for. + /// A that represents the number of milliseconds to wait, or a that represents -1 milliseconds to wait indefinitely. + /// if the operation completed execution within the allotted time; otherwise, . + /// is a negative number other than -1 milliseconds, or is greater than . + /// Thrown is the operation is disposed. + /// + /// + public static bool Wait(this IAsyncOperation op, TimeSpan timeout) + { + var result = true; + + if (!op.IsCompleted) + { + result = op.AsyncWaitHandle.WaitOne(timeout); + } + + if (result) + { + ThrowIfNonSuccess(op); + } + + return result; + } + +#if !NET35 + + /// + /// Waits for the to complete execution. After that rethrows the operation exception (if any). + /// The wait terminates if a cancellation token is canceled before the operation completes. + /// + /// The operation to wait for. + /// A cancellation token to observe while waiting for the operation to complete. + /// Thrown is the operation is disposed. + /// The was canceled. + /// + public static void Wait(this IAsyncOperation op, CancellationToken cancellationToken) + { + WaitInternal(op, cancellationToken); + ThrowIfNonSuccess(op); + } + + /// + /// Waits for the to complete execution within a specified number of milliseconds. After that + /// rethrows the operation exception (if any). The wait terminates if a timeout interval elapses or a cancellation token is + /// canceled before the operation completes. + /// + /// The operation to wait for. + /// The number of milliseconds to wait, or (-1) to wait indefinitely. + /// A cancellation token to observe while waiting for the operation to complete. + /// if the operation completed execution within the allotted time; otherwise, . + /// is a negative number other than -1. + /// The was canceled. + /// Thrown is the operation is disposed. + /// + public static bool Wait(this IAsyncOperation op, int millisecondsTimeout, CancellationToken cancellationToken) + { + if (WaitInternal(op, millisecondsTimeout, cancellationToken)) + { + ThrowIfNonSuccess(op); + return true; + } + + return false; + } + + /// + /// Waits for the to complete execution within a specified time interval. After that rethrows + /// the operation exception (if any). The wait terminates if a timeout interval elapses or a cancellation token is canceled + /// before the operation completes. + /// + /// The operation to wait for. + /// A that represents the number of milliseconds to wait, or a that represents -1 milliseconds to wait indefinitely. + /// A cancellation token to observe while waiting for the operation to complete. + /// if the operation completed execution within the allotted time; otherwise, . + /// is a negative number other than -1 milliseconds, or is greater than . + /// The was canceled. + /// Thrown is the operation is disposed. + /// + public static bool Wait(this IAsyncOperation op, TimeSpan timeout, CancellationToken cancellationToken) + { + if (WaitInternal(op, timeout, cancellationToken)) + { + ThrowIfNonSuccess(op); + return true; + } + + return false; + } + +#endif + + #endregion + + #region Join + + /// + /// Waits for the to complete execution. After that rethrows the operation exception (if any). + /// + /// The operation to join. + /// Thrown is the operation is disposed. + /// + /// + /// + public static void Join(this IAsyncOperation op) + { + if (!op.IsCompleted) + { + op.AsyncWaitHandle.WaitOne(); + } + + ThrowIfNonSuccess(op); + } + + /// + /// Waits for the to complete execution within a specified number of milliseconds. After that rethrows the operation exception (if any). + /// + /// The operation to wait for. + /// The number of milliseconds to wait, or (-1) to wait indefinitely. + /// is a negative number other than -1. + /// Thrown if the operation did not completed within . + /// Thrown is the operation is disposed. + /// + /// + /// + public static void Join(this IAsyncOperation op, int millisecondsTimeout) + { + var result = true; + + if (!op.IsCompleted) + { + result = op.AsyncWaitHandle.WaitOne(millisecondsTimeout); + } + + if (result) + { + ThrowIfNonSuccess(op); + } + else + { + throw new TimeoutException(); + } + } + + /// + /// Waits for the to complete execution within a specified timeout. After that rethrows the operation exception (if any). + /// + /// The operation to wait for. + /// A that represents the number of milliseconds to wait, or a that represents -1 milliseconds to wait indefinitely. + /// is a negative number other than -1 milliseconds, or is greater than . + /// Thrown if the operation did not completed within . + /// Thrown is the operation is disposed. + /// + /// + /// + public static void Join(this IAsyncOperation op, TimeSpan timeout) + { + var result = true; + + if (!op.IsCompleted) + { + result = op.AsyncWaitHandle.WaitOne(timeout); + } + + if (result) + { + ThrowIfNonSuccess(op); + } + else + { + throw new TimeoutException(); + } + } + + /// + /// Waits for the to complete execution. After that rethrows the operation exception (if any). + /// + /// The operation to join. + /// The operation result. + /// Thrown is the operation is disposed. + /// + /// + /// + public static TResult Join(this IAsyncOperation op) + { + if (!op.IsCompleted) + { + op.AsyncWaitHandle.WaitOne(); + } + + return op.Result; + } + + /// + /// Waits for the to complete execution within a specified number of milliseconds. After that rethrows the operation exception (if any). + /// + /// The operation to wait for. + /// The number of milliseconds to wait, or (-1) to wait indefinitely. + /// The operation result. + /// is a negative number other than -1. + /// Thrown if the operation did not completed within . + /// Thrown is the operation is disposed. + /// + /// + /// + public static TResult Join(this IAsyncOperation op, int millisecondsTimeout) + { + var result = true; + + if (!op.IsCompleted) + { + result = op.AsyncWaitHandle.WaitOne(millisecondsTimeout); + } + + if (!result) + { + throw new TimeoutException(); + } + + return op.Result; + } + + /// + /// Waits for the to complete execution within a specified timeout. After that rethrows the operation exception (if any). + /// + /// The operation to wait for. + /// A that represents the number of milliseconds to wait, or a that represents -1 milliseconds to wait indefinitely. + /// The operation result. + /// is a negative number other than -1 milliseconds, or is greater than . + /// Thrown if the operation did not completed within . + /// Thrown is the operation is disposed. + /// + /// + /// + public static TResult Join(this IAsyncOperation op, TimeSpan timeout) + { + var result = true; + + if (!op.IsCompleted) + { + result = op.AsyncWaitHandle.WaitOne(timeout); + } + + if (!result) + { + throw new TimeoutException(); + } + + return op.Result; + } + +#if !NET35 + + /// + /// Waits for the to complete execution. After that rethrows the operation exception (if any). The wait terminates + /// if a cancellation token is canceled before the operation completes. + /// + /// The operation to join. + /// A cancellation token to observe while waiting for the operation to complete. + /// The was canceled. + /// Thrown is the operation is disposed. + /// + public static void Join(this IAsyncOperation op, CancellationToken cancellationToken) + { + WaitInternal(op, cancellationToken); + ThrowIfNonSuccess(op); + } + + /// + /// Waits for the to complete execution within a specified number of milliseconds. After that rethrows the operation exception (if any). + /// The wait terminates if a timeout interval elapses or a cancellation token is canceled before the operation completes. + /// + /// The operation to wait for. + /// The number of milliseconds to wait, or (-1) to wait indefinitely. + /// A cancellation token to observe while waiting for the operation to complete. + /// is a negative number other than -1. + /// Thrown if the operation did not completed within . + /// The was canceled. + /// Thrown is the operation is disposed. + /// + public static void Join(this IAsyncOperation op, int millisecondsTimeout, CancellationToken cancellationToken) + { + if (WaitInternal(op, millisecondsTimeout, cancellationToken)) + { + ThrowIfNonSuccess(op); + } + else + { + throw new TimeoutException(); + } + } + + /// + /// Waits for the to complete execution within a specified timeout. After that rethrows the operation exception (if any). + /// The wait terminates if a timeout interval elapses or a cancellation token is canceled before the operation completes. + /// + /// The operation to wait for. + /// A that represents the number of milliseconds to wait, or a that represents -1 milliseconds to wait indefinitely. + /// A cancellation token to observe while waiting for the operation to complete. + /// is a negative number other than -1 milliseconds, or is greater than . + /// Thrown if the operation did not completed within . + /// The was canceled. + /// Thrown is the operation is disposed. + /// + public static void Join(this IAsyncOperation op, TimeSpan timeout, CancellationToken cancellationToken) + { + if (WaitInternal(op, timeout, cancellationToken)) + { + ThrowIfNonSuccess(op); + } + else + { + throw new TimeoutException(); + } + } + + /// + /// Waits for the to complete execution. After that rethrows the operation exception (if any). + /// The wait terminates if a cancellation token is canceled before the operation completes. + /// + /// The operation to join. + /// A cancellation token to observe while waiting for the operation to complete. + /// The operation result. + /// The was canceled. + /// Thrown is the operation is disposed. + /// + public static TResult Join(this IAsyncOperation op, CancellationToken cancellationToken) + { + WaitInternal(op, cancellationToken); + return op.Result; + } + + /// + /// Waits for the to complete execution within a specified number of milliseconds. After that rethrows the operation exception (if any). + /// The wait terminates if a timeout interval elapses or a cancellation token is canceled before the operation completes. + /// + /// The operation to wait for. + /// The number of milliseconds to wait, or (-1) to wait indefinitely. + /// A cancellation token to observe while waiting for the operation to complete. + /// The operation result. + /// is a negative number other than -1. + /// Thrown if the operation did not completed within . + /// The was canceled. + /// Thrown is the operation is disposed. + /// + public static TResult Join(this IAsyncOperation op, int millisecondsTimeout, CancellationToken cancellationToken) + { + if (!WaitInternal(op, millisecondsTimeout, cancellationToken)) + { + throw new TimeoutException(); + } + + return op.Result; + } + + /// + /// Waits for the to complete execution within a specified timeout. After that rethrows the operation exception (if any). + /// The wait terminates if a timeout interval elapses or a cancellation token is canceled before the operation completes. + /// + /// The operation to wait for. + /// A that represents the number of milliseconds to wait, or a that represents -1 milliseconds to wait indefinitely. + /// A cancellation token to observe while waiting for the operation to complete. + /// The operation result. + /// is a negative number other than -1 milliseconds, or is greater than . + /// Thrown if the operation did not completed within . + /// The was canceled. + /// Thrown is the operation is disposed. + /// + public static TResult Join(this IAsyncOperation op, TimeSpan timeout, CancellationToken cancellationToken) + { + if (!WaitInternal(op, timeout, cancellationToken)) + { + throw new TimeoutException(); + } + + return op.Result; + } + +#endif + + #endregion + + #region ContinueWith + + /// + /// Creates a continuation that executes when the target completes. + /// + /// The operation to continue. + /// An action to run when the completes. + /// Thrown if the is . + /// An operation that is executed after completes. + /// + public static IAsyncOperation ContinueWith(this IAsyncOperation op, Action action) + { + return ContinueWith(op, action, AsyncContinuationOptions.None); + } + + /// + /// Creates a continuation that executes when the target completes. + /// + /// The operation to continue. + /// An action to run when the completes. + /// Options for when the is executed. + /// Thrown if the is . + /// An operation that is executed after completes. + /// + public static IAsyncOperation ContinueWith(this IAsyncOperation op, Action action, AsyncContinuationOptions options) + { + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + + return new ContinueWithResult(op, options, action, null); + } + + /// + /// Creates a continuation that executes when the target completes. + /// + /// The operation to continue. + /// An action to run when the completes. + /// A user-defined state object that is passed as second argument to . + /// Thrown if the is . + /// An operation that is executed after completes. + /// + public static IAsyncOperation ContinueWith(this IAsyncOperation op, Action action, object userState) + { + return ContinueWith(op, action, userState, AsyncContinuationOptions.None); + } + + /// + /// Creates a continuation that executes when the target completes. + /// + /// The operation to continue. + /// An action to run when the completes. + /// A user-defined state object that is passed as second argument to . + /// Options for when the is executed. + /// Thrown if the is . + /// An operation that is executed after completes. + /// + public static IAsyncOperation ContinueWith(this IAsyncOperation op, Action action, object userState, AsyncContinuationOptions options) + { + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + + return new ContinueWithResult(op, options, action, userState); + } + + /// + /// Creates a continuation that executes when the target completes. + /// + /// The operation to continue. + /// An action to run when the completes. + /// Thrown if the is . + /// An operation that is executed after completes. + /// + public static IAsyncOperation ContinueWith(this IAsyncOperation op, Func action) + { + return ContinueWith(op, action, AsyncContinuationOptions.None); + } + + /// + /// Creates a continuation that executes when the target completes. + /// + /// The operation to continue. + /// An action to run when the completes. + /// Options for when the is executed. + /// Thrown if the is . + /// An operation that is executed after completes. + /// + public static IAsyncOperation ContinueWith(this IAsyncOperation op, Func action, AsyncContinuationOptions options) + { + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + + return new ContinueWithResult(op, options, action, null); + } + + /// + /// Creates a continuation that executes when the target completes. + /// + /// The operation to continue. + /// An action to run when the completes. + /// A user-defined state object that is passed as second argument to . + /// Thrown if the is . + /// An operation that is executed after completes. + /// + public static IAsyncOperation ContinueWith(this IAsyncOperation op, Func action, object userState) + { + return ContinueWith(op, action, userState, AsyncContinuationOptions.None); + } + + /// + /// Creates a continuation that executes when the target completes. + /// + /// The operation to continue. + /// An action to run when the completes. + /// A user-defined state object that is passed as second argument to . + /// Options for when the is executed. + /// Thrown if the is . + /// An operation that is executed after completes. + /// + public static IAsyncOperation ContinueWith(this IAsyncOperation op, Func action, object userState, AsyncContinuationOptions options) + { + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + + return new ContinueWithResult(op, options, action, userState); + } + + /// + /// Creates a continuation that executes when the target completes. + /// + /// The operation to continue. + /// An action to run when the completes. + /// Thrown if the is . + /// An operation that is executed after completes. + /// + public static IAsyncOperation ContinueWith(this IAsyncOperation op, Action> action) + { + return ContinueWith(op, action, AsyncContinuationOptions.None); + } + + /// + /// Creates a continuation that executes when the target completes. + /// + /// The operation to continue. + /// An action to run when the completes. + /// Options for when the is executed. + /// Thrown if the is . + /// An operation that is executed after completes. + /// + public static IAsyncOperation ContinueWith(this IAsyncOperation op, Action> action, AsyncContinuationOptions options) + { + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + + return new ContinueWithResult(op, options, action, null); + } + + /// + /// Creates a continuation that executes when the target completes. + /// + /// The operation to continue. + /// An action to run when the completes. + /// A user-defined state object that is passed as second argument to . + /// Thrown if the is . + /// An operation that is executed after completes. + /// + public static IAsyncOperation ContinueWith(this IAsyncOperation op, Action, object> action, object userState) + { + return ContinueWith(op, action, userState, AsyncContinuationOptions.None); + } + + /// + /// Creates a continuation that executes when the target completes. + /// + /// The operation to continue. + /// An action to run when the completes. + /// A user-defined state object that is passed as second argument to . + /// Options for when the is executed. + /// Thrown if the is . + /// An operation that is executed after completes. + /// + public static IAsyncOperation ContinueWith(this IAsyncOperation op, Action, object> action, object userState, AsyncContinuationOptions options) + { + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + + return new ContinueWithResult(op, options, action, userState); + } + + /// + /// Creates a continuation that executes when the target completes. + /// + /// The operation to continue. + /// An action to run when the completes. + /// Thrown if the is . + /// An operation that is executed after completes. + /// + public static IAsyncOperation ContinueWith(this IAsyncOperation op, Func, TNewResult> action) + { + return ContinueWith(op, action, AsyncContinuationOptions.None); + } + + /// + /// Creates a continuation that executes when the target completes. + /// + /// The operation to continue. + /// An action to run when the completes. + /// Options for when the is executed. + /// Thrown if the is . + /// An operation that is executed after completes. + /// + public static IAsyncOperation ContinueWith(this IAsyncOperation op, Func, TNewResult> action, AsyncContinuationOptions options) + { + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + + return new ContinueWithResult(op, options, action, null); + } + + /// + /// Creates a continuation that executes when the target completes. + /// + /// The operation to continue. + /// An action to run when the completes. + /// A user-defined state object that is passed as second argument to . + /// Thrown if the is . + /// An operation that is executed after completes. + /// + public static IAsyncOperation ContinueWith(this IAsyncOperation op, Func, object, TNewResult> action, object userState) + { + return ContinueWith(op, action, userState, AsyncContinuationOptions.None); + } + + /// + /// Creates a continuation that executes when the target completes. + /// + /// The operation to continue. + /// An action to run when the completes. + /// A user-defined state object that is passed as second argument to . + /// Options for when the is executed. + /// Thrown if the is . + /// An operation that is executed after completes. + /// + public static IAsyncOperation ContinueWith(this IAsyncOperation op, Func, object, TNewResult> action, object userState, AsyncContinuationOptions options) + { + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + + return new ContinueWithResult(op, options, action, userState); + } + + #endregion + + #region Unwrap + + /// + /// Creates a proxy that represents the asynchronous operation of a IAsyncOperation<IAsyncOperation>. + /// + /// The source operation. + /// The unwrapped operation. + /// + public static IAsyncOperation Unwrap(this IAsyncOperation op) + { + return new UnwrapResult(op); + } + + /// + /// Creates a proxy that represents the asynchronous operation of a IAsyncOperation<IAsyncOperation<TResult>>. + /// + /// The source operation. + /// The unwrapped operation. + /// + public static IAsyncOperation Unwrap(this IAsyncOperation> op) + { + return new UnwrapResult(op); + } + + #endregion + + #region ToTask + +#if !NET35 + + /// + /// Creates a instance matching the source . + /// + /// The target operation. + /// + public static Task ToTask(this IAsyncOperation op) + { + var status = op.Status; + + if (status == AsyncOperationStatus.RanToCompletion) + { + return Task.CompletedTask; + } + else if (status == AsyncOperationStatus.Faulted) + { + return Task.FromException(op.Exception); + } + else if (status == AsyncOperationStatus.Canceled) + { + return Task.FromCanceled(new CancellationToken(true)); + } + else + { + var tcs = new TaskCompletionSource(); + + op.AddCompletionCallback( + asyncOp => + { + status = op.Status; + + if (status == AsyncOperationStatus.RanToCompletion) + { + tcs.TrySetResult(null); + } + else if (status == AsyncOperationStatus.Faulted) + { + tcs.TrySetException(op.Exception); + } + else if (status == AsyncOperationStatus.Canceled) + { + tcs.TrySetCanceled(); + } + }, + null); + + return tcs.Task; + } + } + + /// + /// Creates a instance matching the source . + /// + /// The target operation. + /// + public static Task ToTask(this IAsyncOperation op) + { + var status = op.Status; + + if (status == AsyncOperationStatus.RanToCompletion) + { + return Task.FromResult(op.Result); + } + else if (status == AsyncOperationStatus.Faulted) + { + return Task.FromException(op.Exception); + } + else if (status == AsyncOperationStatus.Canceled) + { + return Task.FromCanceled(new CancellationToken(true)); + } + else + { + var tcs = new TaskCompletionSource(); + + op.AddCompletionCallback( + asyncOp => + { + status = op.Status; + + if (status == AsyncOperationStatus.RanToCompletion) + { + tcs.TrySetResult(op.Result); + } + else if (status == AsyncOperationStatus.Faulted) + { + tcs.TrySetException(op.Exception); + } + else if (status == AsyncOperationStatus.Canceled) + { + tcs.TrySetCanceled(); + } + }, + null); + + return tcs.Task; + } + } + +#endif + + #endregion + + #region GetAwaiter/ConfigureAwait + +#if !NET35 + + /// + /// Returns the operation awaiter. This method is intended for compiler use only. + /// + /// The operation to await. + /// An object that can be used to await the operation. + public static CompilerServices.AsyncAwaiter GetAwaiter(this IAsyncOperation op) + { + return new CompilerServices.AsyncAwaiter(op); + } + + /// + /// Returns the operation awaiter. This method is intended for compiler use only. + /// + /// The operation to await. + /// An object that can be used to await the operation. + public static CompilerServices.AsyncAwaiter GetAwaiter(this IAsyncOperation op) + { + return new CompilerServices.AsyncAwaiter(op); + } + + /// + /// Configures an awaiter used to await this operation. + /// + /// The operation to await. + /// If attempts to marshal the continuation back to the original context captured. + /// An object that can be used to await the operation. + public static CompilerServices.AsyncAwaitable ConfigureAwait(this IAsyncOperation op, bool continueOnCapturedContext) + { + return new CompilerServices.AsyncAwaitable(op, continueOnCapturedContext ? SynchronizationContext.Current : null); + } + + /// + /// Configures an awaiter used to await this operation. + /// + /// The operation to await. + /// If attempts to marshal the continuation back to the original context captured. + /// An object that can be used to await the operation. + public static CompilerServices.AsyncAwaitable ConfigureAwait(this IAsyncOperation op, bool continueOnCapturedContext) + { + return new CompilerServices.AsyncAwaitable(op, continueOnCapturedContext ? SynchronizationContext.Current : null); + } + + /// + /// Configures an awaiter used to await this operation. + /// + /// The operation to await. + /// Specifies continuation options. + /// An object that can be used to await the operation. + public static CompilerServices.AsyncAwaitable ConfigureAwait(this IAsyncOperation op, AsyncContinuationContext continuationContext) + { + return new CompilerServices.AsyncAwaitable(op, AsyncResult.GetSynchronizationContext(continuationContext)); + } + + /// + /// Configures an awaiter used to await this operation. + /// + /// The operation to await. + /// Specifies continuation options. + /// An object that can be used to await the operation. + public static CompilerServices.AsyncAwaitable ConfigureAwait(this IAsyncOperation op, AsyncContinuationContext continuationContext) + { + return new CompilerServices.AsyncAwaitable(op, AsyncResult.GetSynchronizationContext(continuationContext)); + } + +#endif + + #endregion + + #endregion + + #region IAsyncOperationEvents + + /// + /// Adds a completion callback to be executed after the operation has completed. If the operation is already completed + /// the is called synchronously. + /// + /// + /// The is invoked on a thread that registered the continuation (if it has a attached). + /// Throwing an exception from the callback might cause unspecified behaviour. + /// + /// The operation to schedule continuation for. + /// The callback to be executed when the operation has completed. + /// Thrown if is . + /// Thrown is the operation has been disposed. + public static void AddCompletionCallback(this IAsyncOperationEvents op, Action callback) + { + op.AddCompletionCallback(callback, SynchronizationContext.Current); + } + + /// + /// Adds a completion callback to be executed after the operation has completed. If the operation is already completed + /// the is invoked on a context specified via . + /// + /// + /// The is invoked on a specified. + /// Throwing an exception from the callback might cause unspecified behaviour. + /// + /// The operation to schedule continuation for. + /// The callback to be executed when the operation has completed. + /// Identifier of a to schedule callback on. + /// Thrown if is . + /// Thrown is the operation has been disposed. + public static void AddCompletionCallback(this IAsyncOperationEvents op, Action callback, AsyncContinuationContext continuationContext) + { + op.AddCompletionCallback(callback, AsyncResult.GetSynchronizationContext(continuationContext)); + } + + /// + /// Adds a completion callback to be executed after the operation has completed. If the operation is already completed + /// the is called synchronously. + /// + /// + /// The is invoked on a thread that registered the continuation (if it has a attached). + /// Throwing an exception from the callback might cause unspecified behaviour. + /// + /// The operation to schedule continuation for. + /// The callback to be executed when the operation has completed. + /// Thrown if is . + /// Thrown is the operation has been disposed. + public static void AddCompletionCallback(this IAsyncOperationEvents op, IAsyncContinuation callback) + { + op.AddCompletionCallback(callback, SynchronizationContext.Current); + } + + /// + /// Adds a completion callback to be executed after the operation has completed. If the operation is already completed + /// the is invoked on a context specified via . + /// + /// + /// The is invoked on a specified. + /// Throwing an exception from the callback might cause unspecified behaviour. + /// + /// The operation to schedule continuation for. + /// The callback to be executed when the operation has completed. + /// Identifier of a to schedule callback on. + /// Thrown if is . + /// Thrown is the operation has been disposed. + public static void AddCompletionCallback(this IAsyncOperationEvents op, IAsyncContinuation callback, AsyncContinuationContext continuationContext) + { + op.AddCompletionCallback(callback, AsyncResult.GetSynchronizationContext(continuationContext)); + } + + /// + /// Adds a callback to be executed when the operation progress has changed. If the operation is already completed + /// the is called synchronously. + /// + /// + /// The is invoked on a thread that registered the callback (if it has a attached). + /// Throwing an exception from the callback might cause unspecified behaviour. + /// + /// The operation to schedule continuation for. + /// The callback to be executed when the operation progress has changed. + /// Thrown if is . + /// Thrown is the operation has been disposed. + public static void AddProgressCallback(this IAsyncOperationEvents op, Action callback) + { + op.AddProgressCallback(callback, SynchronizationContext.Current); + } + + /// + /// Adds a callback to be executed when the operation progress has changed. If the operation is already completed + /// the is invoked on a context specified via . + /// + /// + /// The is invoked on a specified. + /// Throwing an exception from the callback might cause unspecified behaviour. + /// + /// The operation to schedule continuation for. + /// The callback to be executed when the operation progress has changed. + /// Identifier of a to schedule callback on. + /// Thrown if is . + /// Thrown is the operation has been disposed. + public static void AddProgressCallback(this IAsyncOperationEvents op, Action callback, AsyncContinuationContext continuationContext) + { + op.AddProgressCallback(callback, AsyncResult.GetSynchronizationContext(continuationContext)); + } + +#if !NET35 + + /// + /// Adds a callback to be executed when the operation progress has changed. If the operation is already completed + /// the is called synchronously. + /// + /// + /// The is invoked on a thread that registered the callback (if it has a attached). + /// Throwing an exception from the callback might cause unspecified behaviour. + /// + /// The operation to schedule continuation for. + /// The callback to be executed when the operation progress has changed. + /// Thrown if is . + /// Thrown is the operation has been disposed. + public static void AddProgressCallback(this IAsyncOperationEvents op, IProgress callback) + { + op.AddProgressCallback(callback, SynchronizationContext.Current); + } + + /// + /// Adds a callback to be executed when the operation progress has changed. If the operation is already completed + /// the is invoked on a context specified via . + /// + /// + /// The is invoked on a specified. + /// Throwing an exception from the callback might cause unspecified behaviour. + /// + /// The operation to schedule continuation for. + /// The callback to be executed when the operation progress has changed. + /// Identifier of a to schedule callback on. + /// Thrown if is . + /// Thrown is the operation has been disposed. + public static void AddProgressCallback(this IAsyncOperationEvents op, IProgress callback, AsyncContinuationContext continuationContext) + { + op.AddProgressCallback(callback, AsyncResult.GetSynchronizationContext(continuationContext)); + } + +#endif + + #endregion + + #region IAsyncCompletionSource + + /// + /// Sets the operation progress value in range [0, 1]. + /// + /// The completion source instance. + /// The operation progress in range [0, 1]. + /// Thrown if is not in range [0, 1]. + /// Thrown if the progress value cannot be set. + /// Thrown is the operation is disposed. + /// + public static void SetProgress(this IAsyncCompletionSource completionSource, float progress) + { + if (!completionSource.TrySetProgress(progress)) + { + throw new InvalidOperationException(Messages.FormatError_OperationStateCannotBeChanged()); + } + } + + /// + /// Sets the operation progress value in range [0, 1]. + /// + /// The completion source instance. + /// The operation progress in range [0, 1]. + /// Thrown if is not in range [0, 1]. + /// Thrown if the progress value cannot be set. + /// Thrown is the operation is disposed. + /// + public static void SetProgress(this IAsyncCompletionSource completionSource, float progress) + { + if (!completionSource.TrySetProgress(progress)) + { + throw new InvalidOperationException(Messages.FormatError_OperationStateCannotBeChanged()); + } + } + + /// + /// Transitions the underlying into the state. + /// + /// The completion source instance. + /// Thrown if the transition fails. + /// Thrown is the operation is disposed. + /// + /// + /// + public static void SetCanceled(this IAsyncCompletionSource completionSource) + { + if (!completionSource.TrySetCanceled()) + { + throw new InvalidOperationException(Messages.FormatError_OperationStateCannotBeChanged()); + } + } + + /// + /// Transitions the underlying into the state. + /// + /// The completion source instance. + /// Thrown if the transition fails. + /// Thrown is the operation is disposed. + /// + /// + /// + public static void SetCanceled(this IAsyncCompletionSource completionSource) + { + if (!completionSource.TrySetCanceled()) + { + throw new InvalidOperationException(Messages.FormatError_OperationStateCannotBeChanged()); + } + } + + /// + /// Attempts to transition the underlying into the state. + /// + /// The completion source instance. + /// An exception message. + /// Thrown if is . + /// Thrown is the operation is disposed. + /// Returns if the attemp was successfull; otherwise. + /// + /// + /// + public static bool TrySetException(this IAsyncCompletionSource completionSource, string message) + { + return completionSource.TrySetException(new Exception(message)); + } + + /// + /// Attempts to transition the underlying into the state. + /// + /// The completion source instance. + /// An exception message. + /// Thrown if is . + /// Thrown is the operation is disposed. + /// Returns if the attemp was successfull; otherwise. + /// + /// + /// + public static bool TrySetException(this IAsyncCompletionSource completionSource, string message) + { + return completionSource.TrySetException(new Exception(message)); + } + + /// + /// Transitions the underlying into the state. + /// + /// The completion source instance. + /// An exception message. + /// Thrown if is . + /// Thrown if the transition fails. + /// Thrown is the operation is disposed. + /// + /// + /// + public static void SetException(this IAsyncCompletionSource completionSource, string message) + { + if (!completionSource.TrySetException(new Exception(message))) + { + throw new InvalidOperationException(Messages.FormatError_OperationStateCannotBeChanged()); + } + } + + /// + /// Transitions the underlying into the state. + /// + /// The completion source instance. + /// An exception message. + /// Thrown if is . + /// Thrown if the transition fails. + /// Thrown is the operation is disposed. + /// + /// + /// + public static void SetException(this IAsyncCompletionSource completionSource, string message) + { + if (!completionSource.TrySetException(new Exception(message))) + { + throw new InvalidOperationException(Messages.FormatError_OperationStateCannotBeChanged()); + } + } + + /// + /// Transitions the underlying into the state. + /// + /// The completion source instance. + /// An exception that caused the operation to end prematurely. + /// Thrown if is . + /// Thrown if the transition fails. + /// Thrown is the operation is disposed. + /// + /// + /// + public static void SetException(this IAsyncCompletionSource completionSource, Exception exception) + { + if (!completionSource.TrySetException(exception)) + { + throw new InvalidOperationException(Messages.FormatError_OperationStateCannotBeChanged()); + } + } + + /// + /// Transitions the underlying into the state. + /// + /// The completion source instance. + /// An exception that caused the operation to end prematurely. + /// Thrown if is . + /// Thrown if the transition fails. + /// Thrown is the operation is disposed. + /// + /// + /// + public static void SetException(this IAsyncCompletionSource completionSource, Exception exception) + { + if (!completionSource.TrySetException(exception)) + { + throw new InvalidOperationException(Messages.FormatError_OperationStateCannotBeChanged()); + } + } + + /// + /// Transitions the underlying into the state. + /// + /// The completion source instance. + /// Exceptions that caused the operation to end prematurely. + /// Thrown if is . + /// Thrown if the transition fails. + /// Thrown is the operation is disposed. + /// + /// + /// + public static void SetExceptions(this IAsyncCompletionSource completionSource, IEnumerable exceptions) + { + if (!completionSource.TrySetExceptions(exceptions)) + { + throw new InvalidOperationException(Messages.FormatError_OperationStateCannotBeChanged()); + } + } + + /// + /// Transitions the underlying into the state. + /// + /// The completion source instance. + /// Exceptions that caused the operation to end prematurely. + /// Thrown if is . + /// Thrown if the transition fails. + /// Thrown is the operation is disposed. + /// + /// + /// + public static void SetExceptions(this IAsyncCompletionSource completionSource, IEnumerable exceptions) + { + if (!completionSource.TrySetExceptions(exceptions)) + { + throw new InvalidOperationException(Messages.FormatError_OperationStateCannotBeChanged()); + } + } + + /// + /// Transitions the underlying into the state. + /// + /// The completion source instance. + /// Thrown if the transition fails. + /// Thrown is the operation is disposed. + /// + /// + /// + public static void SetCompleted(this IAsyncCompletionSource completionSource) + { + if (!completionSource.TrySetCompleted()) + { + throw new InvalidOperationException(Messages.FormatError_OperationStateCannotBeChanged()); + } + } + + /// + /// Transitions the underlying into the state. + /// + /// The completion source instance. + /// The operation result. + /// Thrown if the transition fails. + /// Thrown is the operation is disposed. + /// + /// + /// + public static void SetResult(this IAsyncCompletionSource completionSource, TResult result) + { + if (!completionSource.TrySetResult(result)) + { + throw new InvalidOperationException(Messages.FormatError_OperationStateCannotBeChanged()); + } + } + + #endregion + + #region implementation + +#if !NET35 + + private static void WaitInternal(IAsyncOperation op, CancellationToken cancellationToken) + { + if (!op.IsCompleted) + { + if (cancellationToken.CanBeCanceled) + { + cancellationToken.ThrowIfCancellationRequested(); + + var index = WaitHandle.WaitAny(new WaitHandle[] { op.AsyncWaitHandle, cancellationToken.WaitHandle }); + + if (index == 1) + { + throw new OperationCanceledException(); + } + } + else + { + op.AsyncWaitHandle.WaitOne(); + } + } + } + + private static bool WaitInternal(IAsyncOperation op, int millisecondsTimeout, CancellationToken cancellationToken) + { + var result = true; + + if (!op.IsCompleted) + { + if (cancellationToken.CanBeCanceled) + { + cancellationToken.ThrowIfCancellationRequested(); + + var index = WaitHandle.WaitAny(new WaitHandle[] { op.AsyncWaitHandle, cancellationToken.WaitHandle }, millisecondsTimeout); + + if (index == WaitHandle.WaitTimeout) + { + result = false; + } + else if (index == 1) + { + throw new OperationCanceledException(); + } + } + else + { + result = op.AsyncWaitHandle.WaitOne(millisecondsTimeout); + } + } + + return result; + } + + private static bool WaitInternal(IAsyncOperation op, TimeSpan timeout, CancellationToken cancellationToken) + { + var result = true; + + if (!op.IsCompleted) + { + if (cancellationToken.CanBeCanceled) + { + cancellationToken.ThrowIfCancellationRequested(); + + var index = WaitHandle.WaitAny(new WaitHandle[] { op.AsyncWaitHandle, cancellationToken.WaitHandle }, timeout); + + if (index == WaitHandle.WaitTimeout) + { + result = false; + } + else if (index == 1) + { + throw new OperationCanceledException(); + } + } + else + { + result = op.AsyncWaitHandle.WaitOne(timeout); + } + } + + return result; + } + +#endif + + #endregion + } +} diff --git a/src/UnityFx.Async/Api/AsyncResult.cs b/src/UnityFx.Async/Api/AsyncResult.cs index 9fd0fd5..055fb6e 100644 --- a/src/UnityFx.Async/Api/AsyncResult.cs +++ b/src/UnityFx.Async/Api/AsyncResult.cs @@ -400,7 +400,7 @@ protected internal bool TrySetCanceled(bool completedSynchronously) } else if (!IsCompleted) { - AsyncExtensions.SpinUntilCompleted(this); + SpinUntilCompleted(); } return false; @@ -501,7 +501,7 @@ protected internal bool TrySetException(Exception exception, bool completedSynch } else if (!IsCompleted) { - AsyncExtensions.SpinUntilCompleted(this); + SpinUntilCompleted(); } return false; @@ -591,7 +591,7 @@ protected internal bool TrySetExceptions(IEnumerable exceptions, bool } else if (!IsCompleted) { - AsyncExtensions.SpinUntilCompleted(this); + SpinUntilCompleted(); } return false; @@ -625,7 +625,7 @@ protected internal bool TrySetCompleted(bool completedSynchronously) } else if (!IsCompleted) { - AsyncExtensions.SpinUntilCompleted(this); + SpinUntilCompleted(); } return false; @@ -1362,6 +1362,27 @@ private void NotifyCompleted(AsyncOperationStatus status) } } + private void SpinUntilCompleted() + { +#if NET35 + + while ((_flags & _flagCompleted) == 0) + { + Thread.SpinWait(1); + } + +#else + + var sw = new SpinWait(); + + while ((_flags & _flagCompleted) == 0) + { + sw.SpinOnce(); + } + +#endif + } + #endregion } } diff --git a/src/UnityFx.Async/Api/Extensions/AsyncExtensions.Continuations.cs b/src/UnityFx.Async/Api/Extensions/AsyncExtensions.Continuations.cs deleted file mode 100644 index debf775..0000000 --- a/src/UnityFx.Async/Api/Extensions/AsyncExtensions.Continuations.cs +++ /dev/null @@ -1,305 +0,0 @@ -// Copyright (c) Alexander Bogarsukov. -// Licensed under the MIT license. See the LICENSE.md file in the project root for more information. - -using System; -using System.Threading; - -namespace UnityFx.Async -{ - partial class AsyncExtensions - { - #region ContinueWith - - /// - /// Creates a continuation that executes when the target completes. - /// - /// The operation to continue. - /// An action to run when the completes. - /// Thrown if the is . - /// An operation that is executed after completes. - /// - public static IAsyncOperation ContinueWith(this IAsyncOperation op, Action action) - { - return ContinueWith(op, action, AsyncContinuationOptions.None); - } - - /// - /// Creates a continuation that executes when the target completes. - /// - /// The operation to continue. - /// An action to run when the completes. - /// Options for when the is executed. - /// Thrown if the is . - /// An operation that is executed after completes. - /// - public static IAsyncOperation ContinueWith(this IAsyncOperation op, Action action, AsyncContinuationOptions options) - { - if (action == null) - { - throw new ArgumentNullException(nameof(action)); - } - - return new ContinueWithResult(op, options, action, null); - } - - /// - /// Creates a continuation that executes when the target completes. - /// - /// The operation to continue. - /// An action to run when the completes. - /// A user-defined state object that is passed as second argument to . - /// Thrown if the is . - /// An operation that is executed after completes. - /// - public static IAsyncOperation ContinueWith(this IAsyncOperation op, Action action, object userState) - { - return ContinueWith(op, action, userState, AsyncContinuationOptions.None); - } - - /// - /// Creates a continuation that executes when the target completes. - /// - /// The operation to continue. - /// An action to run when the completes. - /// A user-defined state object that is passed as second argument to . - /// Options for when the is executed. - /// Thrown if the is . - /// An operation that is executed after completes. - /// - public static IAsyncOperation ContinueWith(this IAsyncOperation op, Action action, object userState, AsyncContinuationOptions options) - { - if (action == null) - { - throw new ArgumentNullException(nameof(action)); - } - - return new ContinueWithResult(op, options, action, userState); - } - - /// - /// Creates a continuation that executes when the target completes. - /// - /// The operation to continue. - /// An action to run when the completes. - /// Thrown if the is . - /// An operation that is executed after completes. - /// - public static IAsyncOperation ContinueWith(this IAsyncOperation op, Func action) - { - return ContinueWith(op, action, AsyncContinuationOptions.None); - } - - /// - /// Creates a continuation that executes when the target completes. - /// - /// The operation to continue. - /// An action to run when the completes. - /// Options for when the is executed. - /// Thrown if the is . - /// An operation that is executed after completes. - /// - public static IAsyncOperation ContinueWith(this IAsyncOperation op, Func action, AsyncContinuationOptions options) - { - if (action == null) - { - throw new ArgumentNullException(nameof(action)); - } - - return new ContinueWithResult(op, options, action, null); - } - - /// - /// Creates a continuation that executes when the target completes. - /// - /// The operation to continue. - /// An action to run when the completes. - /// A user-defined state object that is passed as second argument to . - /// Thrown if the is . - /// An operation that is executed after completes. - /// - public static IAsyncOperation ContinueWith(this IAsyncOperation op, Func action, object userState) - { - return ContinueWith(op, action, userState, AsyncContinuationOptions.None); - } - - /// - /// Creates a continuation that executes when the target completes. - /// - /// The operation to continue. - /// An action to run when the completes. - /// A user-defined state object that is passed as second argument to . - /// Options for when the is executed. - /// Thrown if the is . - /// An operation that is executed after completes. - /// - public static IAsyncOperation ContinueWith(this IAsyncOperation op, Func action, object userState, AsyncContinuationOptions options) - { - if (action == null) - { - throw new ArgumentNullException(nameof(action)); - } - - return new ContinueWithResult(op, options, action, userState); - } - - /// - /// Creates a continuation that executes when the target completes. - /// - /// The operation to continue. - /// An action to run when the completes. - /// Thrown if the is . - /// An operation that is executed after completes. - /// - public static IAsyncOperation ContinueWith(this IAsyncOperation op, Action> action) - { - return ContinueWith(op, action, AsyncContinuationOptions.None); - } - - /// - /// Creates a continuation that executes when the target completes. - /// - /// The operation to continue. - /// An action to run when the completes. - /// Options for when the is executed. - /// Thrown if the is . - /// An operation that is executed after completes. - /// - public static IAsyncOperation ContinueWith(this IAsyncOperation op, Action> action, AsyncContinuationOptions options) - { - if (action == null) - { - throw new ArgumentNullException(nameof(action)); - } - - return new ContinueWithResult(op, options, action, null); - } - - /// - /// Creates a continuation that executes when the target completes. - /// - /// The operation to continue. - /// An action to run when the completes. - /// A user-defined state object that is passed as second argument to . - /// Thrown if the is . - /// An operation that is executed after completes. - /// - public static IAsyncOperation ContinueWith(this IAsyncOperation op, Action, object> action, object userState) - { - return ContinueWith(op, action, userState, AsyncContinuationOptions.None); - } - - /// - /// Creates a continuation that executes when the target completes. - /// - /// The operation to continue. - /// An action to run when the completes. - /// A user-defined state object that is passed as second argument to . - /// Options for when the is executed. - /// Thrown if the is . - /// An operation that is executed after completes. - /// - public static IAsyncOperation ContinueWith(this IAsyncOperation op, Action, object> action, object userState, AsyncContinuationOptions options) - { - if (action == null) - { - throw new ArgumentNullException(nameof(action)); - } - - return new ContinueWithResult(op, options, action, userState); - } - - /// - /// Creates a continuation that executes when the target completes. - /// - /// The operation to continue. - /// An action to run when the completes. - /// Thrown if the is . - /// An operation that is executed after completes. - /// - public static IAsyncOperation ContinueWith(this IAsyncOperation op, Func, TNewResult> action) - { - return ContinueWith(op, action, AsyncContinuationOptions.None); - } - - /// - /// Creates a continuation that executes when the target completes. - /// - /// The operation to continue. - /// An action to run when the completes. - /// Options for when the is executed. - /// Thrown if the is . - /// An operation that is executed after completes. - /// - public static IAsyncOperation ContinueWith(this IAsyncOperation op, Func, TNewResult> action, AsyncContinuationOptions options) - { - if (action == null) - { - throw new ArgumentNullException(nameof(action)); - } - - return new ContinueWithResult(op, options, action, null); - } - - /// - /// Creates a continuation that executes when the target completes. - /// - /// The operation to continue. - /// An action to run when the completes. - /// A user-defined state object that is passed as second argument to . - /// Thrown if the is . - /// An operation that is executed after completes. - /// - public static IAsyncOperation ContinueWith(this IAsyncOperation op, Func, object, TNewResult> action, object userState) - { - return ContinueWith(op, action, userState, AsyncContinuationOptions.None); - } - - /// - /// Creates a continuation that executes when the target completes. - /// - /// The operation to continue. - /// An action to run when the completes. - /// A user-defined state object that is passed as second argument to . - /// Options for when the is executed. - /// Thrown if the is . - /// An operation that is executed after completes. - /// - public static IAsyncOperation ContinueWith(this IAsyncOperation op, Func, object, TNewResult> action, object userState, AsyncContinuationOptions options) - { - if (action == null) - { - throw new ArgumentNullException(nameof(action)); - } - - return new ContinueWithResult(op, options, action, userState); - } - - #endregion - - #region Unwrap - - /// - /// Creates a proxy that represents the asynchronous operation of a IAsyncOperation<IAsyncOperation>. - /// - /// The source operation. - /// The unwrapped operation. - /// - public static IAsyncOperation Unwrap(this IAsyncOperation op) - { - return new UnwrapResult(op); - } - - /// - /// Creates a proxy that represents the asynchronous operation of a IAsyncOperation<IAsyncOperation<TResult>>. - /// - /// The source operation. - /// The unwrapped operation. - /// - public static IAsyncOperation Unwrap(this IAsyncOperation> op) - { - return new UnwrapResult(op); - } - - #endregion - } -} diff --git a/src/UnityFx.Async/Api/Extensions/AsyncExtensions.Tasks.cs b/src/UnityFx.Async/Api/Extensions/AsyncExtensions.Tasks.cs deleted file mode 100644 index a270eb9..0000000 --- a/src/UnityFx.Async/Api/Extensions/AsyncExtensions.Tasks.cs +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright (c) Alexander Bogarsukov. -// Licensed under the MIT license. See the LICENSE.md file in the project root for more information. - -using System; -using System.Runtime.CompilerServices; -using System.Threading; -#if !NET35 -using System.Threading.Tasks; -#endif - -namespace UnityFx.Async -{ -#if !NET35 - - partial class AsyncExtensions - { - #region GetAwaiter/ConfigureAwait - - /// - /// Returns the operation awaiter. This method is intended for compiler use only. - /// - /// The operation to await. - /// An object that can be used to await the operation. - public static CompilerServices.AsyncAwaiter GetAwaiter(this IAsyncOperation op) - { - return new CompilerServices.AsyncAwaiter(op); - } - - /// - /// Returns the operation awaiter. This method is intended for compiler use only. - /// - /// The operation to await. - /// An object that can be used to await the operation. - public static CompilerServices.AsyncAwaiter GetAwaiter(this IAsyncOperation op) - { - return new CompilerServices.AsyncAwaiter(op); - } - - /// - /// Configures an awaiter used to await this operation. - /// - /// The operation to await. - /// If attempts to marshal the continuation back to the original context captured. - /// An object that can be used to await the operation. - public static CompilerServices.AsyncAwaitable ConfigureAwait(this IAsyncOperation op, bool continueOnCapturedContext) - { - return new CompilerServices.AsyncAwaitable(op, continueOnCapturedContext ? SynchronizationContext.Current : null); - } - - /// - /// Configures an awaiter used to await this operation. - /// - /// The operation to await. - /// If attempts to marshal the continuation back to the original context captured. - /// An object that can be used to await the operation. - public static CompilerServices.AsyncAwaitable ConfigureAwait(this IAsyncOperation op, bool continueOnCapturedContext) - { - return new CompilerServices.AsyncAwaitable(op, continueOnCapturedContext ? SynchronizationContext.Current : null); - } - - /// - /// Configures an awaiter used to await this operation. - /// - /// The operation to await. - /// Specifies continuation options. - /// An object that can be used to await the operation. - public static CompilerServices.AsyncAwaitable ConfigureAwait(this IAsyncOperation op, AsyncContinuationContext continuationContext) - { - return new CompilerServices.AsyncAwaitable(op, AsyncResult.GetSynchronizationContext(continuationContext)); - } - - /// - /// Configures an awaiter used to await this operation. - /// - /// The operation to await. - /// Specifies continuation options. - /// An object that can be used to await the operation. - public static CompilerServices.AsyncAwaitable ConfigureAwait(this IAsyncOperation op, AsyncContinuationContext continuationContext) - { - return new CompilerServices.AsyncAwaitable(op, AsyncResult.GetSynchronizationContext(continuationContext)); - } - - #endregion - - #region ToTask - - /// - /// Creates a instance matching the source . - /// - /// The target operation. - /// - public static Task ToTask(this IAsyncOperation op) - { - var status = op.Status; - - if (status == AsyncOperationStatus.RanToCompletion) - { - return Task.CompletedTask; - } - else if (status == AsyncOperationStatus.Faulted) - { - return Task.FromException(op.Exception); - } - else if (status == AsyncOperationStatus.Canceled) - { - return Task.FromCanceled(new CancellationToken(true)); - } - else - { - var tcs = new TaskCompletionSource(); - - op.AddCompletionCallback( - asyncOp => - { - status = op.Status; - - if (status == AsyncOperationStatus.RanToCompletion) - { - tcs.TrySetResult(null); - } - else if (status == AsyncOperationStatus.Faulted) - { - tcs.TrySetException(op.Exception); - } - else if (status == AsyncOperationStatus.Canceled) - { - tcs.TrySetCanceled(); - } - }, - null); - - return tcs.Task; - } - } - - /// - /// Creates a instance matching the source . - /// - /// The target operation. - /// - public static Task ToTask(this IAsyncOperation op) - { - var status = op.Status; - - if (status == AsyncOperationStatus.RanToCompletion) - { - return Task.FromResult(op.Result); - } - else if (status == AsyncOperationStatus.Faulted) - { - return Task.FromException(op.Exception); - } - else if (status == AsyncOperationStatus.Canceled) - { - return Task.FromCanceled(new CancellationToken(true)); - } - else - { - var tcs = new TaskCompletionSource(); - - op.AddCompletionCallback( - asyncOp => - { - status = op.Status; - - if (status == AsyncOperationStatus.RanToCompletion) - { - tcs.TrySetResult(op.Result); - } - else if (status == AsyncOperationStatus.Faulted) - { - tcs.TrySetException(op.Exception); - } - else if (status == AsyncOperationStatus.Canceled) - { - tcs.TrySetCanceled(); - } - }, - null); - - return tcs.Task; - } - } - - #endregion - - #region ToAsync - - /// - /// Creates an instance that completes when the specified completes. - /// - /// The task to convert to . - /// An that represents the . - public static AsyncResult ToAsync(this Task task) - { - return AsyncResult.FromTask(task); - } - - /// - /// Creates an instance that completes when the specified completes. - /// - /// The task to convert to . - /// An that represents the . - public static AsyncResult ToAsync(this Task task) - { - return AsyncResult.FromTask(task); - } - - #endregion - - #region implementation - #endregion - } - -#endif -} diff --git a/src/UnityFx.Async/Api/Extensions/AsyncExtensions.Wait.cs b/src/UnityFx.Async/Api/Extensions/AsyncExtensions.Wait.cs deleted file mode 100644 index 20dd438..0000000 --- a/src/UnityFx.Async/Api/Extensions/AsyncExtensions.Wait.cs +++ /dev/null @@ -1,531 +0,0 @@ -// Copyright (c) Alexander Bogarsukov. -// Licensed under the MIT license. See the LICENSE.md file in the project root for more information. - -using System; -using System.Threading; - -namespace UnityFx.Async -{ - partial class AsyncExtensions - { - #region Wait - - /// - /// Waits for the to complete execution. After that rethrows the operation exception (if any). - /// - /// The operation to wait for. - /// Thrown is the operation is disposed. - /// - /// - public static void Wait(this IAsyncOperation op) - { - if (!op.IsCompleted) - { - op.AsyncWaitHandle.WaitOne(); - } - - ThrowIfNonSuccess(op); - } - - /// - /// Waits for the to complete execution within a specified number of milliseconds. After that rethrows the operation exception (if any). - /// - /// The operation to wait for. - /// The number of milliseconds to wait, or (-1) to wait indefinitely. - /// if the operation completed execution within the allotted time; otherwise, . - /// is a negative number other than -1. - /// Thrown is the operation is disposed. - /// - /// - public static bool Wait(this IAsyncOperation op, int millisecondsTimeout) - { - var result = true; - - if (!op.IsCompleted) - { - result = op.AsyncWaitHandle.WaitOne(millisecondsTimeout); - } - - if (result) - { - ThrowIfNonSuccess(op); - } - - return result; - } - - /// - /// Waits for the to complete execution within a specified time interval. After that rethrows the operation exception (if any). - /// - /// The operation to wait for. - /// A that represents the number of milliseconds to wait, or a that represents -1 milliseconds to wait indefinitely. - /// if the operation completed execution within the allotted time; otherwise, . - /// is a negative number other than -1 milliseconds, or is greater than . - /// Thrown is the operation is disposed. - /// - /// - public static bool Wait(this IAsyncOperation op, TimeSpan timeout) - { - var result = true; - - if (!op.IsCompleted) - { - result = op.AsyncWaitHandle.WaitOne(timeout); - } - - if (result) - { - ThrowIfNonSuccess(op); - } - - return result; - } - -#if !NET35 - - /// - /// Waits for the to complete execution. After that rethrows the operation exception (if any). - /// The wait terminates if a cancellation token is canceled before the operation completes. - /// - /// The operation to wait for. - /// A cancellation token to observe while waiting for the operation to complete. - /// Thrown is the operation is disposed. - /// The was canceled. - /// - public static void Wait(this IAsyncOperation op, CancellationToken cancellationToken) - { - WaitInternal(op, cancellationToken); - ThrowIfNonSuccess(op); - } - - /// - /// Waits for the to complete execution within a specified number of milliseconds. After that - /// rethrows the operation exception (if any). The wait terminates if a timeout interval elapses or a cancellation token is - /// canceled before the operation completes. - /// - /// The operation to wait for. - /// The number of milliseconds to wait, or (-1) to wait indefinitely. - /// A cancellation token to observe while waiting for the operation to complete. - /// if the operation completed execution within the allotted time; otherwise, . - /// is a negative number other than -1. - /// The was canceled. - /// Thrown is the operation is disposed. - /// - public static bool Wait(this IAsyncOperation op, int millisecondsTimeout, CancellationToken cancellationToken) - { - if (WaitInternal(op, millisecondsTimeout, cancellationToken)) - { - ThrowIfNonSuccess(op); - return true; - } - - return false; - } - - /// - /// Waits for the to complete execution within a specified time interval. After that rethrows - /// the operation exception (if any). The wait terminates if a timeout interval elapses or a cancellation token is canceled - /// before the operation completes. - /// - /// The operation to wait for. - /// A that represents the number of milliseconds to wait, or a that represents -1 milliseconds to wait indefinitely. - /// A cancellation token to observe while waiting for the operation to complete. - /// if the operation completed execution within the allotted time; otherwise, . - /// is a negative number other than -1 milliseconds, or is greater than . - /// The was canceled. - /// Thrown is the operation is disposed. - /// - public static bool Wait(this IAsyncOperation op, TimeSpan timeout, CancellationToken cancellationToken) - { - if (WaitInternal(op, timeout, cancellationToken)) - { - ThrowIfNonSuccess(op); - return true; - } - - return false; - } - -#endif - - #endregion - - #region Join - - /// - /// Waits for the to complete execution. After that rethrows the operation exception (if any). - /// - /// The operation to join. - /// Thrown is the operation is disposed. - /// - /// - /// - public static void Join(this IAsyncOperation op) - { - if (!op.IsCompleted) - { - op.AsyncWaitHandle.WaitOne(); - } - - ThrowIfNonSuccess(op); - } - - /// - /// Waits for the to complete execution within a specified number of milliseconds. After that rethrows the operation exception (if any). - /// - /// The operation to wait for. - /// The number of milliseconds to wait, or (-1) to wait indefinitely. - /// is a negative number other than -1. - /// Thrown if the operation did not completed within . - /// Thrown is the operation is disposed. - /// - /// - /// - public static void Join(this IAsyncOperation op, int millisecondsTimeout) - { - var result = true; - - if (!op.IsCompleted) - { - result = op.AsyncWaitHandle.WaitOne(millisecondsTimeout); - } - - if (result) - { - ThrowIfNonSuccess(op); - } - else - { - throw new TimeoutException(); - } - } - - /// - /// Waits for the to complete execution within a specified timeout. After that rethrows the operation exception (if any). - /// - /// The operation to wait for. - /// A that represents the number of milliseconds to wait, or a that represents -1 milliseconds to wait indefinitely. - /// is a negative number other than -1 milliseconds, or is greater than . - /// Thrown if the operation did not completed within . - /// Thrown is the operation is disposed. - /// - /// - /// - public static void Join(this IAsyncOperation op, TimeSpan timeout) - { - var result = true; - - if (!op.IsCompleted) - { - result = op.AsyncWaitHandle.WaitOne(timeout); - } - - if (result) - { - ThrowIfNonSuccess(op); - } - else - { - throw new TimeoutException(); - } - } - - /// - /// Waits for the to complete execution. After that rethrows the operation exception (if any). - /// - /// The operation to join. - /// The operation result. - /// Thrown is the operation is disposed. - /// - /// - /// - public static TResult Join(this IAsyncOperation op) - { - if (!op.IsCompleted) - { - op.AsyncWaitHandle.WaitOne(); - } - - return op.Result; - } - - /// - /// Waits for the to complete execution within a specified number of milliseconds. After that rethrows the operation exception (if any). - /// - /// The operation to wait for. - /// The number of milliseconds to wait, or (-1) to wait indefinitely. - /// The operation result. - /// is a negative number other than -1. - /// Thrown if the operation did not completed within . - /// Thrown is the operation is disposed. - /// - /// - /// - public static TResult Join(this IAsyncOperation op, int millisecondsTimeout) - { - var result = true; - - if (!op.IsCompleted) - { - result = op.AsyncWaitHandle.WaitOne(millisecondsTimeout); - } - - if (!result) - { - throw new TimeoutException(); - } - - return op.Result; - } - - /// - /// Waits for the to complete execution within a specified timeout. After that rethrows the operation exception (if any). - /// - /// The operation to wait for. - /// A that represents the number of milliseconds to wait, or a that represents -1 milliseconds to wait indefinitely. - /// The operation result. - /// is a negative number other than -1 milliseconds, or is greater than . - /// Thrown if the operation did not completed within . - /// Thrown is the operation is disposed. - /// - /// - /// - public static TResult Join(this IAsyncOperation op, TimeSpan timeout) - { - var result = true; - - if (!op.IsCompleted) - { - result = op.AsyncWaitHandle.WaitOne(timeout); - } - - if (!result) - { - throw new TimeoutException(); - } - - return op.Result; - } - -#if !NET35 - - /// - /// Waits for the to complete execution. After that rethrows the operation exception (if any). The wait terminates - /// if a cancellation token is canceled before the operation completes. - /// - /// The operation to join. - /// A cancellation token to observe while waiting for the operation to complete. - /// The was canceled. - /// Thrown is the operation is disposed. - /// - public static void Join(this IAsyncOperation op, CancellationToken cancellationToken) - { - WaitInternal(op, cancellationToken); - ThrowIfNonSuccess(op); - } - - /// - /// Waits for the to complete execution within a specified number of milliseconds. After that rethrows the operation exception (if any). - /// The wait terminates if a timeout interval elapses or a cancellation token is canceled before the operation completes. - /// - /// The operation to wait for. - /// The number of milliseconds to wait, or (-1) to wait indefinitely. - /// A cancellation token to observe while waiting for the operation to complete. - /// is a negative number other than -1. - /// Thrown if the operation did not completed within . - /// The was canceled. - /// Thrown is the operation is disposed. - /// - public static void Join(this IAsyncOperation op, int millisecondsTimeout, CancellationToken cancellationToken) - { - if (WaitInternal(op, millisecondsTimeout, cancellationToken)) - { - ThrowIfNonSuccess(op); - } - else - { - throw new TimeoutException(); - } - } - - /// - /// Waits for the to complete execution within a specified timeout. After that rethrows the operation exception (if any). - /// The wait terminates if a timeout interval elapses or a cancellation token is canceled before the operation completes. - /// - /// The operation to wait for. - /// A that represents the number of milliseconds to wait, or a that represents -1 milliseconds to wait indefinitely. - /// A cancellation token to observe while waiting for the operation to complete. - /// is a negative number other than -1 milliseconds, or is greater than . - /// Thrown if the operation did not completed within . - /// The was canceled. - /// Thrown is the operation is disposed. - /// - public static void Join(this IAsyncOperation op, TimeSpan timeout, CancellationToken cancellationToken) - { - if (WaitInternal(op, timeout, cancellationToken)) - { - ThrowIfNonSuccess(op); - } - else - { - throw new TimeoutException(); - } - } - - /// - /// Waits for the to complete execution. After that rethrows the operation exception (if any). - /// The wait terminates if a cancellation token is canceled before the operation completes. - /// - /// The operation to join. - /// A cancellation token to observe while waiting for the operation to complete. - /// The operation result. - /// The was canceled. - /// Thrown is the operation is disposed. - /// - public static TResult Join(this IAsyncOperation op, CancellationToken cancellationToken) - { - WaitInternal(op, cancellationToken); - return op.Result; - } - - /// - /// Waits for the to complete execution within a specified number of milliseconds. After that rethrows the operation exception (if any). - /// The wait terminates if a timeout interval elapses or a cancellation token is canceled before the operation completes. - /// - /// The operation to wait for. - /// The number of milliseconds to wait, or (-1) to wait indefinitely. - /// A cancellation token to observe while waiting for the operation to complete. - /// The operation result. - /// is a negative number other than -1. - /// Thrown if the operation did not completed within . - /// The was canceled. - /// Thrown is the operation is disposed. - /// - public static TResult Join(this IAsyncOperation op, int millisecondsTimeout, CancellationToken cancellationToken) - { - if (!WaitInternal(op, millisecondsTimeout, cancellationToken)) - { - throw new TimeoutException(); - } - - return op.Result; - } - - /// - /// Waits for the to complete execution within a specified timeout. After that rethrows the operation exception (if any). - /// The wait terminates if a timeout interval elapses or a cancellation token is canceled before the operation completes. - /// - /// The operation to wait for. - /// A that represents the number of milliseconds to wait, or a that represents -1 milliseconds to wait indefinitely. - /// A cancellation token to observe while waiting for the operation to complete. - /// The operation result. - /// is a negative number other than -1 milliseconds, or is greater than . - /// Thrown if the operation did not completed within . - /// The was canceled. - /// Thrown is the operation is disposed. - /// - public static TResult Join(this IAsyncOperation op, TimeSpan timeout, CancellationToken cancellationToken) - { - if (!WaitInternal(op, timeout, cancellationToken)) - { - throw new TimeoutException(); - } - - return op.Result; - } - -#endif - - #endregion - - #region implementation - -#if !NET35 - - private static void WaitInternal(IAsyncOperation op, CancellationToken cancellationToken) - { - if (!op.IsCompleted) - { - if (cancellationToken.CanBeCanceled) - { - cancellationToken.ThrowIfCancellationRequested(); - - var index = WaitHandle.WaitAny(new WaitHandle[] { op.AsyncWaitHandle, cancellationToken.WaitHandle }); - - if (index == 1) - { - throw new OperationCanceledException(); - } - } - else - { - op.AsyncWaitHandle.WaitOne(); - } - } - } - - private static bool WaitInternal(IAsyncOperation op, int millisecondsTimeout, CancellationToken cancellationToken) - { - var result = true; - - if (!op.IsCompleted) - { - if (cancellationToken.CanBeCanceled) - { - cancellationToken.ThrowIfCancellationRequested(); - - var index = WaitHandle.WaitAny(new WaitHandle[] { op.AsyncWaitHandle, cancellationToken.WaitHandle }, millisecondsTimeout); - - if (index == WaitHandle.WaitTimeout) - { - result = false; - } - else if (index == 1) - { - throw new OperationCanceledException(); - } - } - else - { - result = op.AsyncWaitHandle.WaitOne(millisecondsTimeout); - } - } - - return result; - } - - private static bool WaitInternal(IAsyncOperation op, TimeSpan timeout, CancellationToken cancellationToken) - { - var result = true; - - if (!op.IsCompleted) - { - if (cancellationToken.CanBeCanceled) - { - cancellationToken.ThrowIfCancellationRequested(); - - var index = WaitHandle.WaitAny(new WaitHandle[] { op.AsyncWaitHandle, cancellationToken.WaitHandle }, timeout); - - if (index == WaitHandle.WaitTimeout) - { - result = false; - } - else if (index == 1) - { - throw new OperationCanceledException(); - } - } - else - { - result = op.AsyncWaitHandle.WaitOne(timeout); - } - } - - return result; - } - -#endif - - #endregion - } -} diff --git a/src/UnityFx.Async/Api/Extensions/AsyncExtensions.cs b/src/UnityFx.Async/Api/Extensions/AsyncExtensions.cs deleted file mode 100644 index d288e85..0000000 --- a/src/UnityFx.Async/Api/Extensions/AsyncExtensions.cs +++ /dev/null @@ -1,983 +0,0 @@ -// Copyright (c) Alexander Bogarsukov. -// Licensed under the MIT license. See the LICENSE.md file in the project root for more information. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.Threading; - -namespace UnityFx.Async -{ - /// - /// Extension methods for . - /// - [EditorBrowsable(EditorBrowsableState.Advanced)] - public static partial class AsyncExtensions - { - #region data - - private static SendOrPostCallback _actionCallback; - -#if !NET35 - - private static Action _cancelHandler; - -#endif - - #endregion - - #region Common - - /// - /// Throws if the specified operation is faulted/canceled. - /// - public static void ThrowIfNonSuccess(this IAsyncOperation op) - { - var status = op.Status; - - if (status == AsyncOperationStatus.Faulted) - { - if (!AsyncResult.TryThrowException(op.Exception)) - { - // Should never get here. Exception should never be null in faulted state. - throw new Exception(); - } - } - else if (status == AsyncOperationStatus.Canceled) - { - if (!AsyncResult.TryThrowException(op.Exception)) - { - throw new OperationCanceledException(); - } - } - } - - /// - /// Creates an that completes when the specified operation completes. - /// - /// The operation to convert to enumerator. - /// An enumerator that represents the operation. - public static IEnumerator ToEnum(this IAsyncResult op) - { - if (op is IEnumerator e) - { - return e; - } - - return new TaskEnumerator(op); - } - - /// - /// Spins until the operation has completed. - /// - /// The operation to wait for. - public static void SpinUntilCompleted(this IAsyncResult op) - { -#if NET35 - - while (!op.IsCompleted) - { - Thread.SpinWait(1); - } - -#else - - var sw = new SpinWait(); - - while (!op.IsCompleted) - { - sw.SpinOnce(); - } - -#endif - } - - /// - /// Spins until the operation has completed within a specified timeout. - /// - /// The operation to wait for. - /// The number of milliseconds to wait, or (-1) to wait indefinitely. - /// is a negative number other than -1. - /// Returns if the operation was completed within the specified time interfval; otherwise. - public static bool SpinUntilCompleted(this IAsyncResult op, int millisecondsTimeout) - { - if (millisecondsTimeout < -1) - { - throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), millisecondsTimeout, Messages.FormatError_InvalidTimeout()); - } - - if (millisecondsTimeout == Timeout.Infinite) - { - SpinUntilCompleted(op); - return true; - } - - return SpinUntilCompletedInternal(op, TimeSpan.FromMilliseconds(millisecondsTimeout)); - } - - /// - /// Spins until the operation has completed within a specified timeout. - /// - /// The operation to wait for. - /// A that represents the number of milliseconds to wait, or a that represents -1 milliseconds to wait indefinitely. - /// is a negative number other than -1 milliseconds, or is greater than . - /// Returns if the operation was completed within the specified time interfval; otherwise. - public static bool SpinUntilCompleted(this IAsyncResult op, TimeSpan timeout) - { - var totalMilliseconds = (long)timeout.TotalMilliseconds; - - if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue) - { - throw new ArgumentOutOfRangeException(nameof(timeout), timeout, Messages.FormatError_InvalidTimeout()); - } - - if (totalMilliseconds == Timeout.Infinite) - { - SpinUntilCompleted(op); - return true; - } - - return SpinUntilCompletedInternal(op, timeout); - } - -#if !NET35 - - /// - /// Spins until the operation has completed or until canceled. - /// - /// The operation to wait for. - /// A cancellation token that can be used to cancel wait operation. - /// The was canceled. - /// - public static void SpinUntilCompleted(this IAsyncResult op, CancellationToken cancellationToken) - { - var sw = new SpinWait(); - - while (!op.IsCompleted) - { - cancellationToken.ThrowIfCancellationRequested(); - sw.SpinOnce(); - } - } - - /// - /// Spins until the operation has completed within a specified timeout or until canceled. - /// - /// The operation to wait for. - /// The number of milliseconds to wait, or (-1) to wait indefinitely. - /// A cancellation token that can be used to cancel wait operation. - /// is a negative number other than -1. - /// The was canceled. - /// Returns if the operation was completed within the specified time interfval; otherwise. - public static bool SpinUntilCompleted(this IAsyncResult op, int millisecondsTimeout, CancellationToken cancellationToken) - { - if (millisecondsTimeout < -1) - { - throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), millisecondsTimeout, Messages.FormatError_InvalidTimeout()); - } - - if (millisecondsTimeout == Timeout.Infinite) - { - SpinUntilCompleted(op, cancellationToken); - return true; - } - - return SpinUntilCompletedInternal(op, TimeSpan.FromMilliseconds(millisecondsTimeout), cancellationToken); - } - - /// - /// Spins until the operation has completed within a specified timeout or until canceled. - /// - /// The operation to wait for. - /// A that represents the number of milliseconds to wait, or a that represents -1 milliseconds to wait indefinitely. - /// A cancellation token that can be used to cancel wait operation. - /// is a negative number other than -1 milliseconds, or is greater than . - /// The was canceled. - /// Returns if the operation was completed within the specified time interfval; otherwise. - public static bool SpinUntilCompleted(this IAsyncResult op, TimeSpan timeout, CancellationToken cancellationToken) - { - var totalMilliseconds = (long)timeout.TotalMilliseconds; - - if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue) - { - throw new ArgumentOutOfRangeException(nameof(timeout), timeout, Messages.FormatError_InvalidTimeout()); - } - - if (totalMilliseconds == Timeout.Infinite) - { - SpinUntilCompleted(op, cancellationToken); - return true; - } - - return SpinUntilCompletedInternal(op, timeout, cancellationToken); - } - - /// - /// Registers a that can be used to cancel the specified operation. - /// - /// An operation to register for. - /// A cancellation token that can be used to cancel the operation. - /// Thrown if the target operation does not support cancellation. - /// Returns the target operation. - public static IAsyncOperation WithCancellation(this IAsyncOperation op, CancellationToken cancellationToken) - { - if (cancellationToken.CanBeCanceled && !op.IsCompleted) - { - if (cancellationToken.IsCancellationRequested) - { - op.Cancel(); - } - else - { - if (_cancelHandler == null) - { - _cancelHandler = args => (args as IAsyncCancellable).Cancel(); - } - - cancellationToken.Register(_cancelHandler, op, false); - } - } - - return op; - } - -#endif - - #endregion - - #region SynchronizationContext - - /// - /// Dispatches an synchronous message to a synchronization context. - /// - /// The target context. - /// The delegate to invoke. - /// Thrown if is . - public static void Send(this SynchronizationContext context, Action action) - { - if (action == null) - { - throw new ArgumentNullException(nameof(action)); - } - - if (_actionCallback == null) - { - _actionCallback = ActionCallback; - } - - context.Post(_actionCallback, action); - } - - /// - /// Dispatches an synchronous message to a synchronization context. - /// - /// The target context. - /// The delegate to invoke. - /// Thrown if is . - /// Returns result of the call. - public static T Send(this SynchronizationContext context, Func action) - { - if (action == null) - { - throw new ArgumentNullException(nameof(action)); - } - - var result = default(T); - - context.Send( - state => - { - result = ((Func)state)(); - }, - action); - - return result; - } - - /// - /// Dispatches an asynchronous message to a synchronization context. - /// - /// The target context. - /// The delegate to invoke. - /// Thrown if is . - public static void Post(this SynchronizationContext context, Action action) - { - if (action == null) - { - throw new ArgumentNullException(nameof(action)); - } - - if (_actionCallback == null) - { - _actionCallback = ActionCallback; - } - - context.Post(_actionCallback, action); - } - - /// - /// Dispatches an asynchronous message to a synchronization context. - /// - /// The target context. - /// The delegate to invoke. - /// Thrown if is . - /// An that can be used to track the operation status. - public static IAsyncOperation PostAsync(this SynchronizationContext context, Action action) - { - if (action == null) - { - throw new ArgumentNullException(nameof(action)); - } - - var op = new ActionResult(action); - - context.Post( - state => - { - ((ActionResult)state).Start(); - }, - op); - - return op; - } - - /// - /// Dispatches an asynchronous message to a synchronization context. - /// - /// The target context. - /// The delegate to invoke. - /// User-defined state. - /// Thrown if is . - /// An that can be used to track the operation status. - public static IAsyncOperation PostAsync(this SynchronizationContext context, SendOrPostCallback d, object state) - { - if (d == null) - { - throw new ArgumentNullException(nameof(d)); - } - - var op = new ActionResult(d, state); - - context.Post( - s => - { - ((ActionResult)s).Start(); - }, - op); - - return op; - } - - /// - /// Dispatches an asynchronous message to a synchronization context. - /// - /// The target context. - /// The delegate to invoke. - /// Thrown if is . - /// An that can be used to track the operation status. - public static IAsyncOperation PostAsync(this SynchronizationContext context, Func action) - { - if (action == null) - { - throw new ArgumentNullException(nameof(action)); - } - - var op = new ActionResult(action); - - context.Post( - state => - { - ((ActionResult)state).Start(); - }, - op); - - return op; - } - - /// - /// Dispatches a message to a synchronization context. - /// - /// The target context. - /// The delegate to invoke. - /// Thrown if is . - public static void Invoke(this SynchronizationContext context, Action action) - { - if (context == SynchronizationContext.Current) - { - if (action == null) - { - throw new ArgumentNullException(nameof(action)); - } - - action.Invoke(); - } - else - { - context.Post(_actionCallback, action); - } - } - - /// - /// Dispatches a message to a synchronization context. - /// - /// The target context. - /// The delegate to invoke. - /// Thrown if is . - /// An that can be used to track the operation status. - public static IAsyncOperation InvokeAsync(this SynchronizationContext context, Action action) - { - if (context == SynchronizationContext.Current) - { - return AsyncResult.FromAction(action); - } - else - { - return PostAsync(context, action); - } - } - - /// - /// Dispatches a message to a synchronization context. - /// - /// The target context. - /// The delegate to invoke. - /// User-defined state. - /// Thrown if is . - public static void Invoke(this SynchronizationContext context, SendOrPostCallback d, object state) - { - if (context == SynchronizationContext.Current) - { - if (d == null) - { - throw new ArgumentNullException(nameof(d)); - } - - d.Invoke(state); - } - else - { - context.Post(d, state); - } - } - - /// - /// Dispatches a message to a synchronization context. - /// - /// The target context. - /// The delegate to invoke. - /// User-defined state. - /// Thrown if is . - /// An that can be used to track the operation status. - public static IAsyncOperation InvokeAsync(this SynchronizationContext context, SendOrPostCallback d, object state) - { - if (context == SynchronizationContext.Current) - { - return AsyncResult.FromAction(d, state); - } - else - { - return PostAsync(context, d, state); - } - } - - /// - /// Dispatches a message to a synchronization context. - /// - /// The target context. - /// The delegate to invoke. - /// Thrown if is . - /// An that can be used to track the operation status. - public static IAsyncOperation InvokeAsync(this SynchronizationContext context, Func action) - { - if (context == SynchronizationContext.Current) - { - return AsyncResult.FromAction(action); - } - else - { - return PostAsync(context, action); - } - } - - #endregion - - #region IAsyncOperationEvents - - /// - /// Adds a completion callback to be executed after the operation has completed. If the operation is already completed - /// the is called synchronously. - /// - /// - /// The is invoked on a thread that registered the continuation (if it has a attached). - /// Throwing an exception from the callback might cause unspecified behaviour. - /// - /// The operation to schedule continuation for. - /// The callback to be executed when the operation has completed. - /// Thrown if is . - /// Thrown is the operation has been disposed. - public static void AddCompletionCallback(this IAsyncOperationEvents op, Action callback) - { - op.AddCompletionCallback(callback, SynchronizationContext.Current); - } - - /// - /// Adds a completion callback to be executed after the operation has completed. If the operation is already completed - /// the is invoked on a context specified via . - /// - /// - /// The is invoked on a specified. - /// Throwing an exception from the callback might cause unspecified behaviour. - /// - /// The operation to schedule continuation for. - /// The callback to be executed when the operation has completed. - /// Identifier of a to schedule callback on. - /// Thrown if is . - /// Thrown is the operation has been disposed. - public static void AddCompletionCallback(this IAsyncOperationEvents op, Action callback, AsyncContinuationContext continuationContext) - { - op.AddCompletionCallback(callback, AsyncResult.GetSynchronizationContext(continuationContext)); - } - - /// - /// Adds a completion callback to be executed after the operation has completed. If the operation is already completed - /// the is called synchronously. - /// - /// - /// The is invoked on a thread that registered the continuation (if it has a attached). - /// Throwing an exception from the callback might cause unspecified behaviour. - /// - /// The operation to schedule continuation for. - /// The callback to be executed when the operation has completed. - /// Thrown if is . - /// Thrown is the operation has been disposed. - public static void AddCompletionCallback(this IAsyncOperationEvents op, IAsyncContinuation callback) - { - op.AddCompletionCallback(callback, SynchronizationContext.Current); - } - - /// - /// Adds a completion callback to be executed after the operation has completed. If the operation is already completed - /// the is invoked on a context specified via . - /// - /// - /// The is invoked on a specified. - /// Throwing an exception from the callback might cause unspecified behaviour. - /// - /// The operation to schedule continuation for. - /// The callback to be executed when the operation has completed. - /// Identifier of a to schedule callback on. - /// Thrown if is . - /// Thrown is the operation has been disposed. - public static void AddCompletionCallback(this IAsyncOperationEvents op, IAsyncContinuation callback, AsyncContinuationContext continuationContext) - { - op.AddCompletionCallback(callback, AsyncResult.GetSynchronizationContext(continuationContext)); - } - - /// - /// Adds a callback to be executed when the operation progress has changed. If the operation is already completed - /// the is called synchronously. - /// - /// - /// The is invoked on a thread that registered the callback (if it has a attached). - /// Throwing an exception from the callback might cause unspecified behaviour. - /// - /// The operation to schedule continuation for. - /// The callback to be executed when the operation progress has changed. - /// Thrown if is . - /// Thrown is the operation has been disposed. - public static void AddProgressCallback(this IAsyncOperationEvents op, Action callback) - { - op.AddProgressCallback(callback, SynchronizationContext.Current); - } - - /// - /// Adds a callback to be executed when the operation progress has changed. If the operation is already completed - /// the is invoked on a context specified via . - /// - /// - /// The is invoked on a specified. - /// Throwing an exception from the callback might cause unspecified behaviour. - /// - /// The operation to schedule continuation for. - /// The callback to be executed when the operation progress has changed. - /// Identifier of a to schedule callback on. - /// Thrown if is . - /// Thrown is the operation has been disposed. - public static void AddProgressCallback(this IAsyncOperationEvents op, Action callback, AsyncContinuationContext continuationContext) - { - op.AddProgressCallback(callback, AsyncResult.GetSynchronizationContext(continuationContext)); - } - -#if !NET35 - - /// - /// Adds a callback to be executed when the operation progress has changed. If the operation is already completed - /// the is called synchronously. - /// - /// - /// The is invoked on a thread that registered the callback (if it has a attached). - /// Throwing an exception from the callback might cause unspecified behaviour. - /// - /// The operation to schedule continuation for. - /// The callback to be executed when the operation progress has changed. - /// Thrown if is . - /// Thrown is the operation has been disposed. - public static void AddProgressCallback(this IAsyncOperationEvents op, IProgress callback) - { - op.AddProgressCallback(callback, SynchronizationContext.Current); - } - - /// - /// Adds a callback to be executed when the operation progress has changed. If the operation is already completed - /// the is invoked on a context specified via . - /// - /// - /// The is invoked on a specified. - /// Throwing an exception from the callback might cause unspecified behaviour. - /// - /// The operation to schedule continuation for. - /// The callback to be executed when the operation progress has changed. - /// Identifier of a to schedule callback on. - /// Thrown if is . - /// Thrown is the operation has been disposed. - public static void AddProgressCallback(this IAsyncOperationEvents op, IProgress callback, AsyncContinuationContext continuationContext) - { - op.AddProgressCallback(callback, AsyncResult.GetSynchronizationContext(continuationContext)); - } - -#endif - - #endregion - - #region IAsyncCompletionSource - - /// - /// Sets the operation progress value in range [0, 1]. - /// - /// The completion source instance. - /// The operation progress in range [0, 1]. - /// Thrown if is not in range [0, 1]. - /// Thrown if the progress value cannot be set. - /// Thrown is the operation is disposed. - /// - public static void SetProgress(this IAsyncCompletionSource completionSource, float progress) - { - if (!completionSource.TrySetProgress(progress)) - { - throw new InvalidOperationException(Messages.FormatError_OperationStateCannotBeChanged()); - } - } - - /// - /// Sets the operation progress value in range [0, 1]. - /// - /// The completion source instance. - /// The operation progress in range [0, 1]. - /// Thrown if is not in range [0, 1]. - /// Thrown if the progress value cannot be set. - /// Thrown is the operation is disposed. - /// - public static void SetProgress(this IAsyncCompletionSource completionSource, float progress) - { - if (!completionSource.TrySetProgress(progress)) - { - throw new InvalidOperationException(Messages.FormatError_OperationStateCannotBeChanged()); - } - } - - /// - /// Transitions the underlying into the state. - /// - /// The completion source instance. - /// Thrown if the transition fails. - /// Thrown is the operation is disposed. - /// - /// - /// - public static void SetCanceled(this IAsyncCompletionSource completionSource) - { - if (!completionSource.TrySetCanceled()) - { - throw new InvalidOperationException(Messages.FormatError_OperationStateCannotBeChanged()); - } - } - - /// - /// Transitions the underlying into the state. - /// - /// The completion source instance. - /// Thrown if the transition fails. - /// Thrown is the operation is disposed. - /// - /// - /// - public static void SetCanceled(this IAsyncCompletionSource completionSource) - { - if (!completionSource.TrySetCanceled()) - { - throw new InvalidOperationException(Messages.FormatError_OperationStateCannotBeChanged()); - } - } - - /// - /// Attempts to transition the underlying into the state. - /// - /// The completion source instance. - /// An exception message. - /// Thrown if is . - /// Thrown is the operation is disposed. - /// Returns if the attemp was successfull; otherwise. - /// - /// - /// - public static bool TrySetException(this IAsyncCompletionSource completionSource, string message) - { - return completionSource.TrySetException(new Exception(message)); - } - - /// - /// Attempts to transition the underlying into the state. - /// - /// The completion source instance. - /// An exception message. - /// Thrown if is . - /// Thrown is the operation is disposed. - /// Returns if the attemp was successfull; otherwise. - /// - /// - /// - public static bool TrySetException(this IAsyncCompletionSource completionSource, string message) - { - return completionSource.TrySetException(new Exception(message)); - } - - /// - /// Transitions the underlying into the state. - /// - /// The completion source instance. - /// An exception message. - /// Thrown if is . - /// Thrown if the transition fails. - /// Thrown is the operation is disposed. - /// - /// - /// - public static void SetException(this IAsyncCompletionSource completionSource, string message) - { - if (!completionSource.TrySetException(new Exception(message))) - { - throw new InvalidOperationException(Messages.FormatError_OperationStateCannotBeChanged()); - } - } - - /// - /// Transitions the underlying into the state. - /// - /// The completion source instance. - /// An exception message. - /// Thrown if is . - /// Thrown if the transition fails. - /// Thrown is the operation is disposed. - /// - /// - /// - public static void SetException(this IAsyncCompletionSource completionSource, string message) - { - if (!completionSource.TrySetException(new Exception(message))) - { - throw new InvalidOperationException(Messages.FormatError_OperationStateCannotBeChanged()); - } - } - - /// - /// Transitions the underlying into the state. - /// - /// The completion source instance. - /// An exception that caused the operation to end prematurely. - /// Thrown if is . - /// Thrown if the transition fails. - /// Thrown is the operation is disposed. - /// - /// - /// - public static void SetException(this IAsyncCompletionSource completionSource, Exception exception) - { - if (!completionSource.TrySetException(exception)) - { - throw new InvalidOperationException(Messages.FormatError_OperationStateCannotBeChanged()); - } - } - - /// - /// Transitions the underlying into the state. - /// - /// The completion source instance. - /// An exception that caused the operation to end prematurely. - /// Thrown if is . - /// Thrown if the transition fails. - /// Thrown is the operation is disposed. - /// - /// - /// - public static void SetException(this IAsyncCompletionSource completionSource, Exception exception) - { - if (!completionSource.TrySetException(exception)) - { - throw new InvalidOperationException(Messages.FormatError_OperationStateCannotBeChanged()); - } - } - - /// - /// Transitions the underlying into the state. - /// - /// The completion source instance. - /// Exceptions that caused the operation to end prematurely. - /// Thrown if is . - /// Thrown if the transition fails. - /// Thrown is the operation is disposed. - /// - /// - /// - public static void SetExceptions(this IAsyncCompletionSource completionSource, IEnumerable exceptions) - { - if (!completionSource.TrySetExceptions(exceptions)) - { - throw new InvalidOperationException(Messages.FormatError_OperationStateCannotBeChanged()); - } - } - - /// - /// Transitions the underlying into the state. - /// - /// The completion source instance. - /// Exceptions that caused the operation to end prematurely. - /// Thrown if is . - /// Thrown if the transition fails. - /// Thrown is the operation is disposed. - /// - /// - /// - public static void SetExceptions(this IAsyncCompletionSource completionSource, IEnumerable exceptions) - { - if (!completionSource.TrySetExceptions(exceptions)) - { - throw new InvalidOperationException(Messages.FormatError_OperationStateCannotBeChanged()); - } - } - - /// - /// Transitions the underlying into the state. - /// - /// The completion source instance. - /// Thrown if the transition fails. - /// Thrown is the operation is disposed. - /// - /// - /// - public static void SetCompleted(this IAsyncCompletionSource completionSource) - { - if (!completionSource.TrySetCompleted()) - { - throw new InvalidOperationException(Messages.FormatError_OperationStateCannotBeChanged()); - } - } - - /// - /// Transitions the underlying into the state. - /// - /// The completion source instance. - /// The operation result. - /// Thrown if the transition fails. - /// Thrown is the operation is disposed. - /// - /// - /// - public static void SetResult(this IAsyncCompletionSource completionSource, TResult result) - { - if (!completionSource.TrySetResult(result)) - { - throw new InvalidOperationException(Messages.FormatError_OperationStateCannotBeChanged()); - } - } - - #endregion - - #region implementation - - private class TaskEnumerator : IEnumerator - { - private readonly IAsyncResult _op; - - public TaskEnumerator(IAsyncResult task) => _op = task; - public object Current => null; - public bool MoveNext() => !_op.IsCompleted; - public void Reset() => throw new NotSupportedException(); - } - -#if !NET35 - - private static bool SpinUntilCompletedInternal(IAsyncResult op, TimeSpan timeout, CancellationToken cancellationToken) - { - var endTime = DateTime.Now + timeout; - var sw = new SpinWait(); - - while (!op.IsCompleted) - { - if (DateTime.Now > endTime) - { - return false; - } - - cancellationToken.ThrowIfCancellationRequested(); - sw.SpinOnce(); - } - - return true; - } - -#endif - - private static bool SpinUntilCompletedInternal(IAsyncResult op, TimeSpan timeout) - { - var endTime = DateTime.Now + timeout; - -#if NET35 - - while (!op.IsCompleted) - { - if (DateTime.Now > endTime) - { - return false; - } - - Thread.SpinWait(1); - } - -#else - - var sw = new SpinWait(); - - while (!op.IsCompleted) - { - if (DateTime.Now > endTime) - { - return false; - } - - sw.SpinOnce(); - } - -#endif - - return true; - } - - private static void ActionCallback(object args) - { - ((Action)args).Invoke(); - } - - #endregion - } -} diff --git a/src/UnityFx.Async/Api/Extensions/IAsyncResultExtensions.cs b/src/UnityFx.Async/Api/Extensions/IAsyncResultExtensions.cs new file mode 100644 index 0000000..0c9fb91 --- /dev/null +++ b/src/UnityFx.Async/Api/Extensions/IAsyncResultExtensions.cs @@ -0,0 +1,255 @@ +// Copyright (c) Alexander Bogarsukov. +// Licensed under the MIT license. See the LICENSE.md file in the project root for more information. + +using System; +using System.Collections; +using System.ComponentModel; +using System.Threading; + +namespace UnityFx.Async.Extensions +{ + /// + /// Extension methods for . + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class IAsyncResultExtensions + { + #region interface + + /// + /// Creates an that completes when the specified operation completes. + /// + /// The operation to convert to enumerator. + /// An enumerator that represents the operation. + public static IEnumerator ToEnum(this IAsyncResult op) + { + if (op is IEnumerator e) + { + return e; + } + + return new TaskEnumerator(op); + } + + /// + /// Spins until the operation has completed. + /// + /// The operation to wait for. + public static void SpinUntilCompleted(this IAsyncResult op) + { +#if NET35 + + while (!op.IsCompleted) + { + Thread.SpinWait(1); + } + +#else + + var sw = new SpinWait(); + + while (!op.IsCompleted) + { + sw.SpinOnce(); + } + +#endif + } + + /// + /// Spins until the operation has completed within a specified timeout. + /// + /// The operation to wait for. + /// The number of milliseconds to wait, or (-1) to wait indefinitely. + /// is a negative number other than -1. + /// Returns if the operation was completed within the specified time interfval; otherwise. + public static bool SpinUntilCompleted(this IAsyncResult op, int millisecondsTimeout) + { + if (millisecondsTimeout < -1) + { + throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), millisecondsTimeout, Messages.FormatError_InvalidTimeout()); + } + + if (millisecondsTimeout == Timeout.Infinite) + { + SpinUntilCompleted(op); + return true; + } + + return SpinUntilCompletedInternal(op, TimeSpan.FromMilliseconds(millisecondsTimeout)); + } + + /// + /// Spins until the operation has completed within a specified timeout. + /// + /// The operation to wait for. + /// A that represents the number of milliseconds to wait, or a that represents -1 milliseconds to wait indefinitely. + /// is a negative number other than -1 milliseconds, or is greater than . + /// Returns if the operation was completed within the specified time interfval; otherwise. + public static bool SpinUntilCompleted(this IAsyncResult op, TimeSpan timeout) + { + var totalMilliseconds = (long)timeout.TotalMilliseconds; + + if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue) + { + throw new ArgumentOutOfRangeException(nameof(timeout), timeout, Messages.FormatError_InvalidTimeout()); + } + + if (totalMilliseconds == Timeout.Infinite) + { + SpinUntilCompleted(op); + return true; + } + + return SpinUntilCompletedInternal(op, timeout); + } + +#if !NET35 + + /// + /// Spins until the operation has completed or until canceled. + /// + /// The operation to wait for. + /// A cancellation token that can be used to cancel wait operation. + /// The was canceled. + /// + public static void SpinUntilCompleted(this IAsyncResult op, CancellationToken cancellationToken) + { + var sw = new SpinWait(); + + while (!op.IsCompleted) + { + cancellationToken.ThrowIfCancellationRequested(); + sw.SpinOnce(); + } + } + + /// + /// Spins until the operation has completed within a specified timeout or until canceled. + /// + /// The operation to wait for. + /// The number of milliseconds to wait, or (-1) to wait indefinitely. + /// A cancellation token that can be used to cancel wait operation. + /// is a negative number other than -1. + /// The was canceled. + /// Returns if the operation was completed within the specified time interfval; otherwise. + public static bool SpinUntilCompleted(this IAsyncResult op, int millisecondsTimeout, CancellationToken cancellationToken) + { + if (millisecondsTimeout < -1) + { + throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), millisecondsTimeout, Messages.FormatError_InvalidTimeout()); + } + + if (millisecondsTimeout == Timeout.Infinite) + { + SpinUntilCompleted(op, cancellationToken); + return true; + } + + return SpinUntilCompletedInternal(op, TimeSpan.FromMilliseconds(millisecondsTimeout), cancellationToken); + } + + /// + /// Spins until the operation has completed within a specified timeout or until canceled. + /// + /// The operation to wait for. + /// A that represents the number of milliseconds to wait, or a that represents -1 milliseconds to wait indefinitely. + /// A cancellation token that can be used to cancel wait operation. + /// is a negative number other than -1 milliseconds, or is greater than . + /// The was canceled. + /// Returns if the operation was completed within the specified time interfval; otherwise. + public static bool SpinUntilCompleted(this IAsyncResult op, TimeSpan timeout, CancellationToken cancellationToken) + { + var totalMilliseconds = (long)timeout.TotalMilliseconds; + + if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue) + { + throw new ArgumentOutOfRangeException(nameof(timeout), timeout, Messages.FormatError_InvalidTimeout()); + } + + if (totalMilliseconds == Timeout.Infinite) + { + SpinUntilCompleted(op, cancellationToken); + return true; + } + + return SpinUntilCompletedInternal(op, timeout, cancellationToken); + } + +#endif + + #endregion + + #region implementation + + private class TaskEnumerator : IEnumerator + { + private readonly IAsyncResult _op; + + public TaskEnumerator(IAsyncResult task) => _op = task; + public object Current => null; + public bool MoveNext() => !_op.IsCompleted; + public void Reset() => throw new NotSupportedException(); + } + +#if !NET35 + + private static bool SpinUntilCompletedInternal(IAsyncResult op, TimeSpan timeout, CancellationToken cancellationToken) + { + var endTime = DateTime.Now + timeout; + var sw = new SpinWait(); + + while (!op.IsCompleted) + { + if (DateTime.Now > endTime) + { + return false; + } + + cancellationToken.ThrowIfCancellationRequested(); + sw.SpinOnce(); + } + + return true; + } + +#endif + + private static bool SpinUntilCompletedInternal(IAsyncResult op, TimeSpan timeout) + { + var endTime = DateTime.Now + timeout; + +#if NET35 + + while (!op.IsCompleted) + { + if (DateTime.Now > endTime) + { + return false; + } + + Thread.SpinWait(1); + } + +#else + + var sw = new SpinWait(); + + while (!op.IsCompleted) + { + if (DateTime.Now > endTime) + { + return false; + } + + sw.SpinOnce(); + } + +#endif + + return true; + } + + #endregion + } +} diff --git a/src/UnityFx.Async/Api/Extensions/AsyncExtensions.Observables.cs b/src/UnityFx.Async/Api/Extensions/IObservableExtensions.cs similarity index 74% rename from src/UnityFx.Async/Api/Extensions/AsyncExtensions.Observables.cs rename to src/UnityFx.Async/Api/Extensions/IObservableExtensions.cs index e052297..a59c8db 100644 --- a/src/UnityFx.Async/Api/Extensions/AsyncExtensions.Observables.cs +++ b/src/UnityFx.Async/Api/Extensions/IObservableExtensions.cs @@ -2,12 +2,17 @@ // Licensed under the MIT license. See the LICENSE.md file in the project root for more information. using System; +using System.ComponentModel; -namespace UnityFx.Async +namespace UnityFx.Async.Extensions { #if !NET35 - partial class AsyncExtensions + /// + /// Extension methods for . + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class IObservableExtensions { /// /// Creates a instance that can be used to track the source observable. diff --git a/src/UnityFx.Async/Api/Extensions/SynchronizationContextExtensions.cs b/src/UnityFx.Async/Api/Extensions/SynchronizationContextExtensions.cs new file mode 100644 index 0000000..c97bbd7 --- /dev/null +++ b/src/UnityFx.Async/Api/Extensions/SynchronizationContextExtensions.cs @@ -0,0 +1,287 @@ +// Copyright (c) Alexander Bogarsukov. +// Licensed under the MIT license. See the LICENSE.md file in the project root for more information. + +using System; +using System.ComponentModel; +using System.Threading; + +namespace UnityFx.Async.Extensions +{ + /// + /// Extension methods for . + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class SynchronizationContextExtensions + { + #region data + + private static SendOrPostCallback _actionCallback; + + #endregion + + #region interface + + /// + /// Dispatches an synchronous message to a synchronization context. + /// + /// The target context. + /// The delegate to invoke. + /// Thrown if is . + public static void Send(this SynchronizationContext context, Action action) + { + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + + if (_actionCallback == null) + { + _actionCallback = ActionCallback; + } + + context.Post(_actionCallback, action); + } + + /// + /// Dispatches an synchronous message to a synchronization context. + /// + /// The target context. + /// The delegate to invoke. + /// Thrown if is . + /// Returns result of the call. + public static T Send(this SynchronizationContext context, Func action) + { + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + + var result = default(T); + + context.Send( + state => + { + result = ((Func)state)(); + }, + action); + + return result; + } + + /// + /// Dispatches an asynchronous message to a synchronization context. + /// + /// The target context. + /// The delegate to invoke. + /// Thrown if is . + public static void Post(this SynchronizationContext context, Action action) + { + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + + if (_actionCallback == null) + { + _actionCallback = ActionCallback; + } + + context.Post(_actionCallback, action); + } + + /// + /// Dispatches an asynchronous message to a synchronization context. + /// + /// The target context. + /// The delegate to invoke. + /// Thrown if is . + /// An that can be used to track the operation status. + public static IAsyncOperation PostAsync(this SynchronizationContext context, Action action) + { + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + + var op = new ActionResult(action); + + context.Post( + state => + { + ((ActionResult)state).Start(); + }, + op); + + return op; + } + + /// + /// Dispatches an asynchronous message to a synchronization context. + /// + /// The target context. + /// The delegate to invoke. + /// User-defined state. + /// Thrown if is . + /// An that can be used to track the operation status. + public static IAsyncOperation PostAsync(this SynchronizationContext context, SendOrPostCallback d, object state) + { + if (d == null) + { + throw new ArgumentNullException(nameof(d)); + } + + var op = new ActionResult(d, state); + + context.Post( + s => + { + ((ActionResult)s).Start(); + }, + op); + + return op; + } + + /// + /// Dispatches an asynchronous message to a synchronization context. + /// + /// The target context. + /// The delegate to invoke. + /// Thrown if is . + /// An that can be used to track the operation status. + public static IAsyncOperation PostAsync(this SynchronizationContext context, Func action) + { + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + + var op = new ActionResult(action); + + context.Post( + state => + { + ((ActionResult)state).Start(); + }, + op); + + return op; + } + + /// + /// Dispatches a message to a synchronization context. + /// + /// The target context. + /// The delegate to invoke. + /// Thrown if is . + public static void Invoke(this SynchronizationContext context, Action action) + { + if (context == SynchronizationContext.Current) + { + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + + action.Invoke(); + } + else + { + context.Post(_actionCallback, action); + } + } + + /// + /// Dispatches a message to a synchronization context. + /// + /// The target context. + /// The delegate to invoke. + /// Thrown if is . + /// An that can be used to track the operation status. + public static IAsyncOperation InvokeAsync(this SynchronizationContext context, Action action) + { + if (context == SynchronizationContext.Current) + { + return AsyncResult.FromAction(action); + } + else + { + return PostAsync(context, action); + } + } + + /// + /// Dispatches a message to a synchronization context. + /// + /// The target context. + /// The delegate to invoke. + /// User-defined state. + /// Thrown if is . + public static void Invoke(this SynchronizationContext context, SendOrPostCallback d, object state) + { + if (context == SynchronizationContext.Current) + { + if (d == null) + { + throw new ArgumentNullException(nameof(d)); + } + + d.Invoke(state); + } + else + { + context.Post(d, state); + } + } + + /// + /// Dispatches a message to a synchronization context. + /// + /// The target context. + /// The delegate to invoke. + /// User-defined state. + /// Thrown if is . + /// An that can be used to track the operation status. + public static IAsyncOperation InvokeAsync(this SynchronizationContext context, SendOrPostCallback d, object state) + { + if (context == SynchronizationContext.Current) + { + return AsyncResult.FromAction(d, state); + } + else + { + return PostAsync(context, d, state); + } + } + + /// + /// Dispatches a message to a synchronization context. + /// + /// The target context. + /// The delegate to invoke. + /// Thrown if is . + /// An that can be used to track the operation status. + public static IAsyncOperation InvokeAsync(this SynchronizationContext context, Func action) + { + if (context == SynchronizationContext.Current) + { + return AsyncResult.FromAction(action); + } + else + { + return PostAsync(context, action); + } + } + + #endregion + + #region implementation + + private static void ActionCallback(object args) + { + ((Action)args).Invoke(); + } + + #endregion + } +} diff --git a/src/UnityFx.Async/Api/Extensions/TaskExtensions.cs b/src/UnityFx.Async/Api/Extensions/TaskExtensions.cs new file mode 100644 index 0000000..39e5358 --- /dev/null +++ b/src/UnityFx.Async/Api/Extensions/TaskExtensions.cs @@ -0,0 +1,42 @@ +// Copyright (c) Alexander Bogarsukov. +// Licensed under the MIT license. See the LICENSE.md file in the project root for more information. + +using System; +using System.ComponentModel; +#if !NET35 +using System.Threading.Tasks; +#endif + +namespace UnityFx.Async.Extensions +{ +#if !NET35 + + /// + /// Extension methods for and . + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class TaskExtensions + { + /// + /// Creates an instance that completes when the specified completes. + /// + /// The task to convert to . + /// An that represents the . + public static AsyncResult ToAsync(this Task task) + { + return AsyncResult.FromTask(task); + } + + /// + /// Creates an instance that completes when the specified completes. + /// + /// The task to convert to . + /// An that represents the . + public static AsyncResult ToAsync(this Task task) + { + return AsyncResult.FromTask(task); + } + } + +#endif +} From 348f5200a6ace25ba8cc590d3649b167644fcd5b Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Sat, 3 Nov 2018 14:11:25 +0200 Subject: [PATCH 32/38] CHANGELOG update --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd4d72f..8a64b8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/); this proj ## [0.9.8] - unreleased ### Added -- `AsyncResult` is now Task-like type and can be used as `async` methd result value (requires C# 7.2). +- `AsyncResult` is now Task-like type and can be used as `async` method result value (requires C# 7.2). - Added new `AsyncResult.FromAction` overloads. +- Added new `SynchronizationContext` extension methods (`PostAsync`, `InvokeAsync` etc). + +### Changed +- Moved BCL extension methods to namespace `UnityFx.Async.Extensions` (previously they were in namespace `UnityFx.Async`). ### Fixed - Fixed `AsyncResult` completion callbacks to be called event if `OnCompleted` throws. From 9407f5eb9f43ae8af6378a6b07519b3e3fd0e4fe Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Sat, 3 Nov 2018 15:12:23 +0200 Subject: [PATCH 33/38] Added Stream extensions --- .../Api/Extensions/StreamExtensions.cs | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 src/UnityFx.Async/Api/Extensions/StreamExtensions.cs diff --git a/src/UnityFx.Async/Api/Extensions/StreamExtensions.cs b/src/UnityFx.Async/Api/Extensions/StreamExtensions.cs new file mode 100644 index 0000000..3d7ced0 --- /dev/null +++ b/src/UnityFx.Async/Api/Extensions/StreamExtensions.cs @@ -0,0 +1,124 @@ +// Copyright (c) Alexander Bogarsukov. +// Licensed under the MIT license. See the LICENSE.md file in the project root for more information. + +using System; +using System.ComponentModel; +using System.IO; + +namespace UnityFx.Async.Extensions +{ + /// + /// Extension methods for class. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class StreamExtensions + { + #region interface + + /// + /// Asynchronously reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. + /// + /// The stream to read data from. + /// The buffer to write the data into. + /// The byte offset in at which to begin writing data from the stream. + /// The maximum number of bytes to read. + /// Thrown if is . + /// Thrown if or is negative. + /// Thrown if the sum of and is larger than the length. + /// Thrown if the stream does not support reading. + /// Thrown if the stream is currently in use by a previous read operation. + /// Thrown if the stream has been disposed. + /// An that represents the asynchronous read operation. The value of the result + /// parameter contains the total number of bytes read into the buffer. The result value can be less than the number of bytes requested + /// if the number of bytes currently available is less than the requested number, or it can be 0 (zero) if the end of the stream + /// has been reached. + public static IAsyncOperation ReadAsync(this Stream stream, byte[] buffer, int offset, int count) + { + var op = new ReadResult(stream); + stream.BeginRead(buffer, offset, count, OnReadCompleted, op); + return op; + } + + /// + /// Asynchronously writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. + /// + /// The stream to write data to. + /// The buffer to write data from. + /// The zero-based byte offset in from which to begin copying bytes to the stream. + /// The maximum number of bytes to write. + /// Thrown if is . + /// Thrown if or is negative. + /// Thrown if the sum of and is larger than the length. + /// Thrown if the stream does not support writing. + /// Thrown if the stream is currently in use by a previous write operation. + /// Thrown if the stream has been disposed. + /// An that represents the asynchronous write operation. + public static IAsyncOperation WriteAsync(this Stream stream, byte[] buffer, int offset, int count) + { + var op = new WriteResult(stream); + stream.BeginWrite(buffer, offset, count, OnWriteCompleted, op); + return op; + } + + #endregion + + #region implementation + + private class ReadResult : AsyncResult + { + private readonly Stream _stream; + + public ReadResult(Stream stream) + : base(AsyncOperationStatus.Running) + { + } + + public void SetCompleted(IAsyncResult op) + { + try + { + TrySetResult(_stream.EndRead(op)); + } + catch (Exception e) + { + TrySetException(e); + } + } + } + + private static void OnReadCompleted(IAsyncResult op) + { + ((ReadResult)op.AsyncState).SetCompleted(op); + } + + private class WriteResult : AsyncResult + { + private readonly Stream _stream; + + public WriteResult(Stream stream) + : base(AsyncOperationStatus.Running) + { + } + + public void SetCompleted(IAsyncResult op) + { + try + { + _stream.EndWrite(op); + TrySetCompleted(); + } + catch (Exception e) + { + TrySetException(e); + } + } + } + + private static void OnWriteCompleted(IAsyncResult op) + { + ((WriteResult)op.AsyncState).SetCompleted(op); + } + + #endregion + } +} From 2d26ddd49a3b44057656cc36af0f44bdb6c4ae3c Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Sat, 3 Nov 2018 15:43:43 +0200 Subject: [PATCH 34/38] Added WebRequest extension methods --- .../Api/Extensions/WebRequestExtensions.cs | 79 +++++++++++++++++++ .../Specialized/ApmResult{TSource,TResult}.cs | 18 +++++ 2 files changed, 97 insertions(+) create mode 100644 src/UnityFx.Async/Api/Extensions/WebRequestExtensions.cs create mode 100644 src/UnityFx.Async/Implementation/Specialized/ApmResult{TSource,TResult}.cs diff --git a/src/UnityFx.Async/Api/Extensions/WebRequestExtensions.cs b/src/UnityFx.Async/Api/Extensions/WebRequestExtensions.cs new file mode 100644 index 0000000..b1db555 --- /dev/null +++ b/src/UnityFx.Async/Api/Extensions/WebRequestExtensions.cs @@ -0,0 +1,79 @@ +// Copyright (c) Alexander Bogarsukov. +// Licensed under the MIT license. See the LICENSE.md file in the project root for more information. + +using System; +using System.ComponentModel; +using System.IO; +using System.Net; + +namespace UnityFx.Async.Extensions +{ + /// + /// Extension methods for class. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class WebRequestExtensions + { + #region interface + + /// + /// Returns a for writing data to the Internet resource as an asynchronous operation. + /// + /// The source . + /// Thrown if an attempt is made to access the method, when the method is not overridden in a descendant class. + /// Returns representing the asynchronous operation. + public static IAsyncOperation GetRequestStreamAsync(this WebRequest webRequest) + { + var op = new ApmResult(webRequest); + webRequest.BeginGetRequestStream(OnGetRequestStreamCompleted, op); + return op; + } + + /// + /// Begins an asynchronous request for an Internet resource. + /// + /// The source . + /// Thrown if an attempt is made to access the method, when the method is not overridden in a descendant class. + /// Returns representing the asynchronous operation. + public static IAsyncOperation GetResponseAsync(this WebRequest webRequest) + { + var op = new ApmResult(webRequest); + webRequest.BeginGetResponse(OnGetResponseCompleted, op); + return op; + } + + #endregion + + #region implementation + + private static void OnGetRequestStreamCompleted(IAsyncResult asyncResult) + { + var op = (ApmResult)asyncResult.AsyncState; + + try + { + op.TrySetResult(op.Source.EndGetRequestStream(asyncResult)); + } + catch (Exception e) + { + op.TrySetException(e); + } + } + + private static void OnGetResponseCompleted(IAsyncResult asyncResult) + { + var op = (ApmResult)asyncResult.AsyncState; + + try + { + op.TrySetResult(op.Source.EndGetResponse(asyncResult)); + } + catch (Exception e) + { + op.TrySetException(e); + } + } + + #endregion + } +} diff --git a/src/UnityFx.Async/Implementation/Specialized/ApmResult{TSource,TResult}.cs b/src/UnityFx.Async/Implementation/Specialized/ApmResult{TSource,TResult}.cs new file mode 100644 index 0000000..3c2944c --- /dev/null +++ b/src/UnityFx.Async/Implementation/Specialized/ApmResult{TSource,TResult}.cs @@ -0,0 +1,18 @@ +// Copyright (c) Alexander Bogarsukov. +// Licensed under the MIT license. See the LICENSE.md file in the project root for more information. + +using System; + +namespace UnityFx.Async +{ + internal class ApmResult : AsyncResult + { + public TSource Source { get; } + + public ApmResult(TSource source) + : base(AsyncOperationStatus.Running) + { + Source = source; + } + } +} From 7df4ce356b9362a7fd39585fc07e381916b809f3 Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Sat, 3 Nov 2018 15:44:11 +0200 Subject: [PATCH 35/38] Minor code refactoring --- .../Api/Extensions/StreamExtensions.cs | 56 +++++-------------- .../Specialized/ActionResult{T}.cs | 2 - 2 files changed, 15 insertions(+), 43 deletions(-) diff --git a/src/UnityFx.Async/Api/Extensions/StreamExtensions.cs b/src/UnityFx.Async/Api/Extensions/StreamExtensions.cs index 3d7ced0..73eff9b 100644 --- a/src/UnityFx.Async/Api/Extensions/StreamExtensions.cs +++ b/src/UnityFx.Async/Api/Extensions/StreamExtensions.cs @@ -34,7 +34,7 @@ public static class StreamExtensions /// has been reached. public static IAsyncOperation ReadAsync(this Stream stream, byte[] buffer, int offset, int count) { - var op = new ReadResult(stream); + var op = new ApmResult(stream); stream.BeginRead(buffer, offset, count, OnReadCompleted, op); return op; } @@ -55,7 +55,7 @@ public static IAsyncOperation ReadAsync(this Stream stream, byte[] buffer, /// An that represents the asynchronous write operation. public static IAsyncOperation WriteAsync(this Stream stream, byte[] buffer, int offset, int count) { - var op = new WriteResult(stream); + var op = new ApmResult(stream); stream.BeginWrite(buffer, offset, count, OnWriteCompleted, op); return op; } @@ -64,61 +64,35 @@ public static IAsyncOperation WriteAsync(this Stream stream, byte[] buffer, int #region implementation - private class ReadResult : AsyncResult + private static void OnReadCompleted(IAsyncResult asyncResult) { - private readonly Stream _stream; + var op = (ApmResult)asyncResult.AsyncState; - public ReadResult(Stream stream) - : base(AsyncOperationStatus.Running) + try { + op.TrySetResult(op.Source.EndRead(asyncResult)); } - - public void SetCompleted(IAsyncResult op) + catch (Exception e) { - try - { - TrySetResult(_stream.EndRead(op)); - } - catch (Exception e) - { - TrySetException(e); - } + op.TrySetException(e); } } - private static void OnReadCompleted(IAsyncResult op) - { - ((ReadResult)op.AsyncState).SetCompleted(op); - } - - private class WriteResult : AsyncResult + private static void OnWriteCompleted(IAsyncResult asyncResult) { - private readonly Stream _stream; + var op = (ApmResult)asyncResult.AsyncState; - public WriteResult(Stream stream) - : base(AsyncOperationStatus.Running) + try { + op.Source.EndWrite(asyncResult); + op.TrySetCompleted(); } - - public void SetCompleted(IAsyncResult op) + catch (Exception e) { - try - { - _stream.EndWrite(op); - TrySetCompleted(); - } - catch (Exception e) - { - TrySetException(e); - } + op.TrySetException(e); } } - private static void OnWriteCompleted(IAsyncResult op) - { - ((WriteResult)op.AsyncState).SetCompleted(op); - } - #endregion } } diff --git a/src/UnityFx.Async/Implementation/Specialized/ActionResult{T}.cs b/src/UnityFx.Async/Implementation/Specialized/ActionResult{T}.cs index 815ba1c..ccf8246 100644 --- a/src/UnityFx.Async/Implementation/Specialized/ActionResult{T}.cs +++ b/src/UnityFx.Async/Implementation/Specialized/ActionResult{T}.cs @@ -2,8 +2,6 @@ // Licensed under the MIT license. See the LICENSE.md file in the project root for more information. using System; -using System.Diagnostics; -using System.Threading; namespace UnityFx.Async { From c7356937ba3337ea1c8ba562c711eb90b723966c Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Sun, 4 Nov 2018 17:32:25 +0200 Subject: [PATCH 36/38] Added Socket extension methods --- .../Api/Extensions/SocketExtensions.cs | 366 ++++++++++++++++++ 1 file changed, 366 insertions(+) create mode 100644 src/UnityFx.Async/Api/Extensions/SocketExtensions.cs diff --git a/src/UnityFx.Async/Api/Extensions/SocketExtensions.cs b/src/UnityFx.Async/Api/Extensions/SocketExtensions.cs new file mode 100644 index 0000000..e55b02d --- /dev/null +++ b/src/UnityFx.Async/Api/Extensions/SocketExtensions.cs @@ -0,0 +1,366 @@ +// Copyright (c) Alexander Bogarsukov. +// Licensed under the MIT license. See the LICENSE.md file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Net; +using System.Net.Sockets; + +namespace UnityFx.Async.Extensions +{ + /// + /// Extension methods for class. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class SocketExtensions + { + #region interface + + /// + /// Begins an asynchronous operation to accept an incoming connection attempt from a specified socket and + /// receives the first block of data sent by the client application. + /// + /// The target socket. + /// The accepted object. This value may be . + /// The maximum number of bytes to receive. + /// An error occurred when attempting to access the . + /// Thrown if the has been closed. + /// Returns representing the asynchronous operation. + public static IAsyncOperation AcceptAsync(this Socket socket, Socket acceptSocket, int receiveSize) + { + var op = new ApmResult(socket); + socket.BeginAccept(acceptSocket, receiveSize, OnAcceptCompleted, op); + return op; + } + + /// + /// Begins an asynchronous operation to accept an incoming connection attempt and receives the first block of data + /// sent by the client application. + /// + /// The target socket. + /// The maximum number of bytes to receive. + /// An error occurred when attempting to access the . + /// Thrown if the has been closed. + /// Returns representing the asynchronous operation. + public static IAsyncOperation AcceptAsync(this Socket socket, int receiveSize) + { + var op = new ApmResult(socket); + socket.BeginAccept(receiveSize, OnAcceptCompleted, op); + return op; + } + + /// + /// Begins an asynchronous operation to accept an incoming connection attempt. + /// + /// The target socket. + /// An error occurred when attempting to access the . + /// Thrown if the has been closed. + /// Returns representing the asynchronous operation. + public static IAsyncOperation AcceptAsync(this Socket socket) + { + var op = new ApmResult(socket); + socket.BeginAccept(OnAcceptCompleted, op); + return op; + } + + /// + /// Begins an asynchronous request for a remote host connection. + /// + /// The target socket. + /// An that represents the remote host. + /// Thrown if is . + /// The is listening. + /// An error occurred when attempting to access the . + /// Thrown if the has been closed. + /// Returns representing the asynchronous operation. + public static IAsyncOperation ConnectAsync(this Socket socket, EndPoint remoteEP) + { + var op = new ApmResult(socket); + socket.BeginConnect(remoteEP, OnConnectCompleted, op); + return op; + } + + /// + /// Begins an asynchronous request for a remote host connection. The host is specified by an and a port number. + /// + /// The target socket. + /// The of the remote host. + /// The port number of the remote host. + /// Thrown if is . + /// Thrown if number is invalid. + /// The is listening. + /// An error occurred when attempting to access the . + /// Thrown if the has been closed. + /// Returns representing the asynchronous operation. + public static IAsyncOperation ConnectAsync(this Socket socket, IPAddress address, int port) + { + var op = new ApmResult(socket); + socket.BeginConnect(address, port, OnConnectCompleted, op); + return op; + } + + /// + /// Begins an asynchronous request for a remote host connection. The host is specified by a host name and a port number. + /// + /// The target socket. + /// The name of the remote host. + /// The port number of the remote host. + /// Thrown if is . + /// Thrown if number is invalid. + /// The is listening. + /// An error occurred when attempting to access the . + /// Thrown if the has been closed. + /// Returns representing the asynchronous operation. + public static IAsyncOperation ConnectAsync(this Socket socket, string host, int port) + { + var op = new ApmResult(socket); + socket.BeginConnect(host, port, OnConnectCompleted, op); + return op; + } + + /// + /// Begins an asynchronous request for a remote host connection. The host is specified by an array and a port number. + /// + /// The target socket. + /// At least one , designating the remote host. + /// The port number of the remote host. + /// Thrown if is . + /// Thrown if number is invalid. + /// The is listening. + /// An error occurred when attempting to access the . + /// Thrown if the has been closed. + /// Returns representing the asynchronous operation. + public static IAsyncOperation ConnectAsync(this Socket socket, IPAddress[] addresses, int port) + { + var op = new ApmResult(socket); + socket.BeginConnect(addresses, port, OnConnectCompleted, op); + return op; + } + + /// + /// Begins an asynchronous request to disconnect from a remote endpoint. + /// + /// The target socket. + /// if this socket can be reused after the connection is closed; otherwise, . + /// An error occurred when attempting to access the . + /// The operating system is Windows 2000 or earlier, and this method requires Windows XP. + /// Thrown if the has been closed. + /// Returns representing the asynchronous operation. + public static IAsyncOperation DisconnectAsync(this Socket socket, bool reuseSocket) + { + var op = new ApmResult(socket); + socket.BeginDisconnect(reuseSocket, OnDisconnectCompleted, op); + return op; + } + + /// + /// Sends data asynchronously to a connected . + /// + /// The target socket. + /// An array of bytes that is the storage location for the received data. + /// A bitwise combination of the values. + /// Thrown if is . + /// An error occurred when attempting to access the . + /// Thrown if the has been closed. + /// Returns representing the asynchronous operation. + public static IAsyncOperation SendAsync(this Socket socket, IList> buffers, SocketFlags socketFlags) + { + var op = new ApmResult(socket); + socket.BeginSend(buffers, socketFlags, OnSendCompleted, op); + return op; + } + + /// + /// Sends data asynchronously to a connected . + /// + /// The target socket. + /// An array of bytes that contains the data to send. + /// The zero-based position in the at which to begin sending data. + /// The number of bytes to send. + /// A bitwise combination of the values. + /// Thrown if is . + /// Thrown if is less than 0 or is less + /// than the length of or is less than 0 or is greater + /// than the length of minus the value of the parameter. + /// An error occurred when attempting to access the . + /// Thrown if the has been closed. + /// Returns representing the asynchronous operation. + public static IAsyncOperation SendAsync(this Socket socket, byte[] buffer, int offset, int size, SocketFlags socketFlags) + { + var op = new ApmResult(socket); + socket.BeginSend(buffer, offset, size, socketFlags, OnSendCompleted, op); + return op; + } + + /// + /// Sends the file to a connected object using the flag. + /// + /// The target socket. + /// A string that contains the path and name of the file to send. This parameter can be . + /// The file was not found. + /// An error occurred when attempting to access the . + /// The operating system is not Windows NT or later or rhe is not connected to a remote host. + /// Thrown if the has been closed. + /// Returns representing the asynchronous operation. + public static IAsyncOperation SendFileAsync(this Socket socket, string fileName) + { + var op = new ApmResult(socket); + socket.BeginSendFile(fileName, OnSendFileCompleted, op); + return op; + } + + /// + /// Sends the file to a connected object. + /// + /// The target socket. + /// A string that contains the path and name of the file to send. This parameter can be . + /// A byte array that contains data to be sent before the file is sent. This parameter can be . + /// A byte array that contains data to be sent after the file is sent. This parameter can be . + /// A bitwise combination of values. + /// The file was not found. + /// An error occurred when attempting to access the . + /// The operating system is not Windows NT or later or rhe is not connected to a remote host. + /// Thrown if the has been closed. + /// Returns representing the asynchronous operation. + public static IAsyncOperation SendFileAsync(this Socket socket, string fileName, byte[] preBuffer, byte[] postBuffer, TransmitFileOptions flags) + { + var op = new ApmResult(socket); + socket.BeginSendFile(fileName, preBuffer, postBuffer, flags, OnSendFileCompleted, op); + return op; + } + + /// + /// Begins to asynchronously receive data from a connected . + /// + /// The target socket. + /// An array of bytes that is the storage location for the received data. + /// A bitwise combination of the values. + /// Thrown if is . + /// An error occurred when attempting to access the . + /// Thrown if the has been closed. + /// Returns representing the asynchronous operation. + public static IAsyncOperation ReceiveAsync(this Socket socket, IList> buffers, SocketFlags socketFlags) + { + var op = new ApmResult(socket); + socket.BeginReceive(buffers, socketFlags, OnReceiveCompleted, op); + return op; + } + + /// + /// Begins to asynchronously receive data from a connected . + /// + /// The target socket. + /// An array of bytes that is the storage location for the received data. + /// The zero-based position in the at which to store the received data. + /// The number of bytes to receive. + /// A bitwise combination of the values. + /// Thrown if is . + /// Thrown if is less than 0 or is greater + /// than the length of or is less than 0 or is greater + /// than the length of minus the value of the parameter. + /// An error occurred when attempting to access the . + /// Thrown if the has been closed. + /// Returns representing the asynchronous operation. + public static IAsyncOperation ReceiveAsync(this Socket socket, byte[] buffer, int offset, int size, SocketFlags socketFlags) + { + var op = new ApmResult(socket); + socket.BeginReceive(buffer, offset, size, socketFlags, OnReceiveCompleted, op); + return op; + } + + #endregion + + #region implementation + + private static void OnAcceptCompleted(IAsyncResult asyncResult) + { + var op = (ApmResult)asyncResult.AsyncState; + + try + { + op.TrySetResult(op.Source.EndAccept(asyncResult)); + } + catch (Exception e) + { + op.TrySetException(e); + } + } + + private static void OnConnectCompleted(IAsyncResult asyncResult) + { + var op = (ApmResult)asyncResult.AsyncState; + + try + { + op.Source.EndConnect(asyncResult); + op.TrySetCompleted(); + } + catch (Exception e) + { + op.TrySetException(e); + } + } + + private static void OnDisconnectCompleted(IAsyncResult asyncResult) + { + var op = (ApmResult)asyncResult.AsyncState; + + try + { + op.Source.EndDisconnect(asyncResult); + op.TrySetCompleted(); + } + catch (Exception e) + { + op.TrySetException(e); + } + } + + private static void OnSendCompleted(IAsyncResult asyncResult) + { + var op = (ApmResult)asyncResult.AsyncState; + + try + { + op.TrySetResult(op.Source.EndSend(asyncResult)); + } + catch (Exception e) + { + op.TrySetException(e); + } + } + + private static void OnSendFileCompleted(IAsyncResult asyncResult) + { + var op = (ApmResult)asyncResult.AsyncState; + + try + { + op.Source.EndSendFile(asyncResult); + op.TrySetCompleted(); + } + catch (Exception e) + { + op.TrySetException(e); + } + } + + private static void OnReceiveCompleted(IAsyncResult asyncResult) + { + var op = (ApmResult)asyncResult.AsyncState; + + try + { + op.TrySetResult(op.Source.EndReceive(asyncResult)); + } + catch (Exception e) + { + op.TrySetException(e); + } + } + + #endregion + } +} From d66779cd0ac0bd7dd33e4a73f8faee115d85bb4a Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Sun, 4 Nov 2018 17:37:10 +0200 Subject: [PATCH 37/38] CHANGELOG update --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a64b8f..a58fb0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/); this proj - `AsyncResult` is now Task-like type and can be used as `async` method result value (requires C# 7.2). - Added new `AsyncResult.FromAction` overloads. - Added new `SynchronizationContext` extension methods (`PostAsync`, `InvokeAsync` etc). +- Added extension methods for `Socket`, `WebRequest`, `Stream` BCL classes. ### Changed - Moved BCL extension methods to namespace `UnityFx.Async.Extensions` (previously they were in namespace `UnityFx.Async`). From aca8ccc9f79b43623204d76253cb81f6456d0a4a Mon Sep 17 00:00:00 2001 From: Alexander Bogarsukov Date: Fri, 9 Nov 2018 21:50:24 +0200 Subject: [PATCH 38/38] CHANGELOG update --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a58fb0b..236752b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/); this project adheres to [Semantic Versioning](http://semver.org/). ----------------------- -## [0.9.8] - unreleased +## [0.9.8] - 2018.11.09 ### Added - `AsyncResult` is now Task-like type and can be used as `async` method result value (requires C# 7.2).