Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Unityで使える非同期ライブラリアップします

  • Loading branch information...
commit 44737236edc11c2f8b59eec15ab2becf86b647c5 1 parent cbbd83e
ppc authored
Showing with 2,465 additions and 6 deletions.
  1. +13 −6 .gitignore
  2. +56 −0 AimingLib.sln
  3. +10 −0 AimingLib.vssscc
  4. +6 −0 BasicSample/App.config
  5. +74 −0 BasicSample/BasicSample.cs
  6. +75 −0 BasicSample/BasicSample.csproj
  7. +27 −0 BasicSample/Common.cs
  8. +107 −0 BasicSample/ContinuationSample.cs
  9. +12 −0 BasicSample/Program.cs
  10. +36 −0 BasicSample/Properties/AssemblyInfo.cs
  11. +1 −0  CopyToAssets.bat
  12. +30 −0 CopyToAssets.ps1
  13. +58 −0 IteratorTasks/CancellationToken.cs
  14. +52 −0 IteratorTasks/CancellationTokenSource.cs
  15. +233 −0 IteratorTasks/ContinuationTask.cs
  16. +16 −0 IteratorTasks/IProgress.cs
  17. +67 −0 IteratorTasks/IteratorTasks.csproj
  18. +86 −0 IteratorTasks/MultiTask.cs
  19. +26 −0 IteratorTasks/Progress.cs
  20. +36 −0 IteratorTasks/Properties/AssemblyInfo.cs
  21. +412 −0 IteratorTasks/Task.cs
  22. +11 −0 IteratorTasks/TaskCanceledException.cs
  23. +12 −0 IteratorTasks/TaskScheduler.cs
  24. +31 −0 IteratorTasks/TaskStatus.cs
  25. +35 −0 IteratorTasks/Util.cs
  26. +36 −0 SampleTaskRunner/Properties/AssemblyInfo.cs
  27. +63 −0 SampleTaskRunner/SampleTaskRunner.csproj
  28. +46 −0 SampleTaskRunner/TaskRunner.cs
  29. +146 −0 TestIteratorTasks/CancellationTokenTest.cs
  30. +152 −0 TestIteratorTasks/Coroutines.cs
  31. +49 −0 TestIteratorTasks/ProgressTest.cs
  32. +36 −0 TestIteratorTasks/Properties/AssemblyInfo.cs
  33. +314 −0 TestIteratorTasks/TaskTest.cs
  34. +101 −0 TestIteratorTasks/TestIteratorTasks.csproj
View
19 .gitignore
@@ -1,6 +1,13 @@
-# Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
-bin
-obj
-
-# mstest test results
-TestResults
+*.pidb
+*.suo
+*.userprefs
+*.vsmdi
+*.testsettings
+*/bin
+*/obj
+*/publish
+$tf
+TestResults
+!*.sln
+!*.csproj
+!*/*.csproj
View
56 AimingLib.sln
@@ -0,0 +1,56 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IteratorTasks", "IteratorTasks\IteratorTasks.csproj", "{3AB99F84-7E6F-4B01-9F57-AE5722C56893}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleTaskRunner", "SampleTaskRunner\SampleTaskRunner.csproj", "{5BF3A7F6-634B-4B1E-B3B2-134ECF8B1B26}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BasicSample", "BasicSample\BasicSample.csproj", "{B2D257A2-953D-434E-8056-C32E5F2D7489}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestIteratorTasks", "TestIteratorTasks\TestIteratorTasks.csproj", "{A3FBC7EE-46C0-4A22-BD44-FDFCE425672D}"
+EndProject
+Global
+ GlobalSection(TeamFoundationVersionControl) = preSolution
+ SccNumberOfProjects = 5
+ SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C}
+ SccTeamFoundationServer = https://tfs.codeplex.com/tfs/tfs37
+ SccLocalPath0 = .
+ SccProjectUniqueName1 = BasicSample\\BasicSample.csproj
+ SccProjectName1 = BasicSample
+ SccLocalPath1 = BasicSample
+ SccProjectUniqueName2 = IteratorTasks\\IteratorTasks.csproj
+ SccProjectName2 = IteratorTasks
+ SccLocalPath2 = IteratorTasks
+ SccProjectUniqueName3 = SampleTaskRunner\\SampleTaskRunner.csproj
+ SccProjectName3 = SampleTaskRunner
+ SccLocalPath3 = SampleTaskRunner
+ SccProjectUniqueName4 = TestIteratorTasks\\TestIteratorTasks.csproj
+ SccProjectName4 = TestIteratorTasks
+ SccLocalPath4 = TestIteratorTasks
+ EndGlobalSection
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {3AB99F84-7E6F-4B01-9F57-AE5722C56893}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3AB99F84-7E6F-4B01-9F57-AE5722C56893}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3AB99F84-7E6F-4B01-9F57-AE5722C56893}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3AB99F84-7E6F-4B01-9F57-AE5722C56893}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5BF3A7F6-634B-4B1E-B3B2-134ECF8B1B26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5BF3A7F6-634B-4B1E-B3B2-134ECF8B1B26}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5BF3A7F6-634B-4B1E-B3B2-134ECF8B1B26}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5BF3A7F6-634B-4B1E-B3B2-134ECF8B1B26}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B2D257A2-953D-434E-8056-C32E5F2D7489}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B2D257A2-953D-434E-8056-C32E5F2D7489}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B2D257A2-953D-434E-8056-C32E5F2D7489}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B2D257A2-953D-434E-8056-C32E5F2D7489}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A3FBC7EE-46C0-4A22-BD44-FDFCE425672D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A3FBC7EE-46C0-4A22-BD44-FDFCE425672D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A3FBC7EE-46C0-4A22-BD44-FDFCE425672D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A3FBC7EE-46C0-4A22-BD44-FDFCE425672D}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
View
10 AimingLib.vssscc
@@ -0,0 +1,10 @@
+""
+{
+"FILE_VERSION" = "9237"
+"ENLISTMENT_CHOICE" = "NEVER"
+"PROJECT_FILE_RELATIVE_PATH" = ""
+"NUMBER_OF_EXCLUDED_FILES" = "0"
+"ORIGINAL_PROJECT_FILE_PATH" = ""
+"NUMBER_OF_NESTED_PROJECTS" = "0"
+"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROJECT"
+}
View
6 BasicSample/App.config
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<configuration>
+ <startup>
+
+ <supportedRuntime version="v2.0.50727"/></startup>
+</configuration>
View
74 BasicSample/BasicSample.cs
@@ -0,0 +1,74 @@
+using System;
+using Aiming.IteratorTasks;
+
+namespace Sample
+{
+ /// <summary>
+ /// 3つのタスクを同時に動かす例。
+ /// </summary>
+ class BasicSample
+ {
+ public static void Run()
+ {
+ var runner = new SampleTaskRunner.TaskRunner();
+
+ Common.ShowFrameTask(50).Start(runner);
+
+ new Task<string>(Worker1)
+ .OnComplete(t => Console.WriteLine("Worker 1 Done: " + t.Result))
+ .Start(runner);
+
+ new Task<int>(Worker2)
+ .OnComplete(t => Console.WriteLine("Worker 2 Done: " + t.Result))
+ .Start(runner);
+
+ runner.Update(200);
+ }
+
+ /// <summary>
+ /// 30フレーム掛けて何かやった体で、文字列を返すコルーチン。
+ ///
+ /// 1フレームに処理が集中しないように分割して実行するイメージ。
+ /// </summary>
+ /// <param name="completed"></param>
+ /// <returns></returns>
+ private static System.Collections.IEnumerator Worker1(Action<string> completed)
+ {
+ Console.WriteLine("Start Worker 1");
+
+ for (int i = 0; i < 30; i++)
+ {
+ yield return null;
+ }
+
+ completed("Result");
+ }
+
+ /// <summary>
+ /// 3秒掛けて何かやった体で、数値を返すコルーチン。
+ ///
+ /// スレッドを立ててスリープしている部分を、時間がかかる計算や、ネットワーク待ちに置き換えて考えていただけると。
+ /// </summary>
+ /// <param name="completed"></param>
+ /// <returns></returns>
+ private static System.Collections.IEnumerator Worker2(Action<int> completed)
+ {
+ bool done = false;
+ int result = 0;
+
+ System.Threading.ThreadPool.QueueUserWorkItem(state =>
+ {
+ System.Threading.Thread.Sleep(3000);
+ result = 999;
+ done = true;
+ });
+
+ Console.WriteLine("Start Worker 2");
+
+ while (!done)
+ yield return null;
+
+ completed(result);
+ }
+ }
+}
View
75 BasicSample/BasicSample.csproj
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{B2D257A2-953D-434E-8056-C32E5F2D7489}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>BasicSample</RootNamespace>
+ <AssemblyName>BasicSample</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <TargetFrameworkProfile />
+ <SccProjectName>SAK</SccProjectName>
+ <SccLocalPath>SAK</SccLocalPath>
+ <SccAuxPath>SAK</SccAuxPath>
+ <SccProvider>SAK</SccProvider>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="BasicSample.cs" />
+ <Compile Include="Common.cs" />
+ <Compile Include="ContinuationSample.cs" />
+ <Compile Include="Program.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="App.config" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\IteratorTasks\IteratorTasks.csproj">
+ <Project>{3ab99f84-7e6f-4b01-9f57-ae5722c56893}</Project>
+ <Name>IteratorTasks</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\SampleTaskRunner\SampleTaskRunner.csproj">
+ <Project>{5bf3a7f6-634b-4b1e-b3b2-134ecf8b1b26}</Project>
+ <Name>SampleTaskRunner</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
View
27 BasicSample/Common.cs
@@ -0,0 +1,27 @@
+using System;
+using Aiming.IteratorTasks;
+
+namespace Sample
+{
+ class Common
+ {
+ public static Task ShowFrameTask(int numFrames)
+ {
+ return new Task(() => ShowFrame(numFrames));
+ }
+
+ /// <summary>
+ /// 毎フレーム、500ミリ秒スリープして、フレーム数を表示する。
+ /// </summary>
+ /// <returns></returns>
+ public static System.Collections.IEnumerator ShowFrame(int numFrames)
+ {
+ for (int i = 0; i < numFrames; i++)
+ {
+ System.Threading.Thread.Sleep(500);
+ Console.WriteLine("frame: {0}", i);
+ yield return null;
+ }
+ }
+ }
+}
View
107 BasicSample/ContinuationSample.cs
@@ -0,0 +1,107 @@
+using System;
+using Aiming.IteratorTasks;
+
+namespace Sample
+{
+ /// <summary>
+ /// 継続処理のサンプル。
+ ///
+ /// 同期処理とイテレーター非同期処理の対応関係を説明。
+ /// U F(T x) → IEnumerator FAsync(T x, Action&lt;U&lt;);
+ /// F2(F1(x)) → new Task&lt;U&lt;(c => F1Async(x, c).ContinueWith&lt;V&lt;(F2Async);
+ /// </summary>
+ class ContinuationSample
+ {
+ public static void Run()
+ {
+ var x = 1.41421356;
+
+ // 同期処理
+ var result = F3(F2(F1(x)));
+ Console.WriteLine("同期処理の結果: " + result);
+
+ // イテレーター非同期処理
+ var task = new Task<double>(c => F1Async(x, c))
+ .ContinueWith<string>(F2Async)
+ .ContinueWith<int>(F3Async)
+ .OnComplete(t => Console.WriteLine("非同期処理の結果: " + t.Result));
+
+ var runner = new SampleTaskRunner.TaskRunner();
+
+ task.Start(runner);
+ Common.ShowFrameTask(50).Start(runner);
+
+ runner.Update(20);
+ }
+
+ #region 同期処理
+
+ // 中身には深い意味なし。
+ // 単に、F3(F2(F1(x))) みたいに繋ぎたいだけ。
+
+ private static double F1(double x)
+ {
+ return x * x;
+ }
+
+ private static string F2(double x)
+ {
+ return x.ToString();
+ }
+
+ private static int F3(string s)
+ {
+ return s.Length;
+ }
+
+ #endregion
+ #region イテレーター非同期処理
+
+ /// <summary>
+ /// 5フレーム後にF1の結果を返す。
+ /// </summary>
+ private static System.Collections.IEnumerator F1Async(double x, Action<double> completed)
+ {
+ for (int i = 0; i < 5; i++)
+ {
+ yield return null;
+ }
+
+ Console.WriteLine("F1Async 終了");
+ var result = F1(x);
+ completed(result);
+ }
+
+ /// <summary>
+ /// 5フレーム後にF2の結果を返す。
+ /// </summary>
+ private static System.Collections.IEnumerator F2Async(double x, Action<string> completed)
+ {
+ for (int i = 0; i < 5; i++)
+ {
+ yield return null;
+ }
+
+ Console.WriteLine("F2Async 終了");
+ var result = F2(x);
+ completed(result);
+ }
+
+ /// <summary>
+ /// 5フレーム後にF3の結果を返す。
+ /// </summary>
+ private static System.Collections.IEnumerator F3Async(string s, Action<int> completed)
+ {
+ for (int i = 0; i < 5; i++)
+ {
+ yield return null;
+ }
+
+ Console.WriteLine("F3Async 終了");
+ var result = F3(s);
+ completed(result);
+ }
+
+ #endregion
+ }
+}
View
12 BasicSample/Program.cs
@@ -0,0 +1,12 @@
+
+namespace Sample
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ ContinuationSample.Run();
+ //BasicSample.Run();
+ }
+ }
+}
View
36 BasicSample/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// アセンブリに関する一般情報は以下の属性セットをとおして制御されます。
+// アセンブリに関連付けられている情報を変更するには、
+// これらの属性値を変更してください。
+[assembly: AssemblyTitle("BasicSample")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("BasicSample")]
+[assembly: AssemblyCopyright("Copyright © 2012")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// ComVisible を false に設定すると、その型はこのアセンブリ内で COM コンポーネントから
+// 参照不可能になります。COM からこのアセンブリ内の型にアクセスする場合は、
+// その型の ComVisible 属性を true に設定してください。
+[assembly: ComVisible(false)]
+
+// 次の GUID は、このプロジェクトが COM に公開される場合の、typelib の ID です
+[assembly: Guid("9afc6961-1dbb-43d6-b762-1f617622ea2f")]
+
+// アセンブリのバージョン情報は、以下の 4 つの値で構成されています:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// すべての値を指定するか、下のように '*' を使ってビルドおよびリビジョン番号を
+// 既定値にすることができます:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
View
1  CopyToAssets.bat
@@ -0,0 +1 @@
+PowerShell -Command "./CopyToAssets.ps1"
View
30 CopyToAssets.ps1
@@ -0,0 +1,30 @@
+# AimingLib 以下のコードを Assets/Aiming にコピー
+# Test から始まるフォルダーと、Sample から始まるフォルダーは除外
+# .cs ファイルと .meta ファイル以外は削除
+
+$testProject = 'Test*'
+$sampleProject = 'Sample*'
+$excludeFolders = 'Properties', 'bin', 'obj'
+
+$from = '.'
+$to = '..\Assets\Aiming'
+
+$projects = ls |
+ ?{ -not ($_.Name -like $testProject) } |
+ ?{ -not ($_.Name -like $sampleProject) } |
+ ?{ $_ -is [IO.DirectoryInfo] }
+
+# いったん丸ごとコピー
+foreach($p in $projects)
+{
+ copy $p.FullName $to -Force -Recurse
+}
+
+# bin とかのフォルダーはじき
+foreach($ex in $excludeFolders)
+{
+ ls $to -Recurse | ?{ $_.Name -eq $ex } | ?{ $_ -is [IO.DirectoryInfo] } | rm -Recurse
+}
+
+# .cs 以外不要なので削除
+ls $to -Recurse -Force | ?{ ($_.Extension -ne '.cs') -and ($_.Extension -ne '.meta') } | ?{ $_ -is [IO.FileInfo] } | rm -Force
View
58 IteratorTasks/CancellationToken.cs
@@ -0,0 +1,58 @@
+using System;
+
+namespace Aiming.IteratorTasks
+{
+ /// <summary>
+ /// タスクのキャンセル用トークン。
+ /// キャンセルを受ける側。
+ /// </summary>
+ public struct CancellationToken
+ {
+ private CancellationTokenSource _source;
+
+ internal CancellationToken(CancellationTokenSource source) { _source = source; }
+
+ /// <summary>
+ /// キャンセル要求が出ているかどうか。
+ /// </summary>
+ public bool IsCancellationRequested
+ {
+ get
+ {
+ if (_source == null)
+ return false;
+ else
+ return _source.IsCancellationRequested;
+ }
+ }
+
+ /// <summary>
+ /// キャンセル要求時に通知を受け取るためのデリゲートを登録。
+ /// </summary>
+ /// <param name="onCanceled">キャンセル要求時に呼ばれるデリゲート。</param>
+ public void Register(Action onCanceled)
+ {
+ if (_source != null)
+ _source.Canceled += onCanceled;
+ }
+
+ /// <summary>
+ /// 空のトークン。
+ /// </summary>
+ public static CancellationToken None = new CancellationToken();
+
+ /// <summary>
+ /// キャンセル要求が出ている場合、OperationCanceledException をスローする。
+ /// </summary>
+ public void ThrowIfCancellationRequested()
+ {
+ if (IsCancellationRequested)
+ {
+ var e = _source.CancelReason != null ?
+ _source.CancelReason :
+ new TaskCanceledException();
+ throw e;
+ }
+ }
+ }
+}
View
52 IteratorTasks/CancellationTokenSource.cs
@@ -0,0 +1,52 @@
+using System;
+
+namespace Aiming.IteratorTasks
+{
+ /// <summary>
+ /// タスクのキャンセル用トークン。
+ /// キャンセルする側。
+ /// </summary>
+ public class CancellationTokenSource
+ {
+ public CancellationTokenSource()
+ {
+ Token = new CancellationToken(this);
+ }
+
+ /// <summary>
+ /// キャンセル用トークン。
+ /// </summary>
+ public CancellationToken Token { get; private set; }
+
+ /// <summary>
+ /// キャンセル要求を出したかどうか。
+ /// </summary>
+ public bool IsCancellationRequested { get; private set; }
+
+ /// <summary>
+ /// キャンセル。
+ /// </summary>
+ public void Cancel()
+ {
+ var d = Canceled;
+ if (d != null) d();
+
+ IsCancellationRequested = true;
+ }
+
+ /// <summary>
+ /// キャンセルの原因となる例外を指定してのキャンセル要求。
+ /// </summary>
+ public void Cancel(Exception cancelReason)
+ {
+ _cancelReason = cancelReason;
+
+ this.Cancel();
+ }
+
+ internal event Action Canceled;
+
+ private Exception _cancelReason;
+ internal Exception CancelReason { get { return _cancelReason; } }
+ }
+}
View
233 IteratorTasks/ContinuationTask.cs
@@ -0,0 +1,233 @@
+using System;
+using System.Collections;
+
+namespace Aiming.IteratorTasks
+{
+ public partial class Task
+ {
+ /// <summary>
+ /// 1つのタスク完了後に、続けて処理をする新しいタスクを生成。
+ /// </summary>
+ /// <param name="continuation">継続処理のタスク生成メソッド。</param>
+ /// <returns>新しいタスク。</returns>
+ public Task ContinueWithTask(Func<Task> continuation)
+ {
+ return new ContinuationTask(this, continuation);
+ }
+
+ /// <summary>
+ /// 1つのタスク完了後に、続けて処理をする新しいタスクを生成。
+ /// </summary>
+ /// <param name="continuation">継続処理のコルーチン生成メソッド。</param>
+ /// <returns>新しいタスク。</returns>
+ public Task ContinueWith(Func<IEnumerator> continuation)
+ {
+ return this.ContinueWithTask(() => new Task(continuation()));
+ }
+
+ /// <summary>
+ /// 1つのタスク完了後に、続けて処理をする新しいタスクを生成。
+ /// </summary>
+ /// <typeparam name="U">継続タスクの戻り値の型。</typeparam>
+ /// <param name="continuation">継続処理のタスク生成メソッド。</param>
+ /// <returns>新しいタスク。</returns>
+ public Task<U> ContinueWithTask<U>(Func<Task<U>> continuation)
+ {
+ return new ContinuationTask<U>(this, continuation);
+ }
+
+ /// <summary>
+ /// 1つのタスク完了後に、続けて処理をする新しいタスクを生成。
+ /// </summary>
+ /// <typeparam name="U">継続タスクの戻り値の型。</typeparam>
+ /// <param name="continuation">継続処理のコルーチン生成メソッド。</param>
+ /// <returns>新しいタスク。</returns>
+ public Task<U> ContinueWith<U>(Func<Action<U>, IEnumerator> continuation)
+ {
+ return this.ContinueWithTask(() => new Task<U>(continuation));
+ }
+ }
+
+ public partial class Task<T>
+ {
+ /// <summary>
+ /// 1つのタスク完了後に、続けて処理をする新しいタスクを生成。
+ /// </summary>
+ /// <param name="continuation">継続処理のタスク生成メソッド。</param>
+ /// <returns>新しいタスク。</returns>
+ public Task ContinueWithTask(Func<T, Task> continuation)
+ {
+ return new ContinuationTask(this, () =>
+ {
+ var res = this.Result;
+ this._result = default(T);
+ GC.Collect();
+ return continuation(res);
+ });
+ }
+
+ /// <summary>
+ /// 1つのタスク完了後に、続けて処理をする新しいタスクを生成。
+ /// </summary>
+ /// <param name="continuation">継続処理のコルーチン生成メソッド。</param>
+ /// <returns>新しいタスク。</returns>
+ public Task ContinueWith(Func<T, IEnumerator> continuation)
+ {
+ return this.ContinueWithTask(() => new Task(continuation(this.Result)));
+ }
+
+ /// <summary>
+ /// 1つのタスク完了後に、続けて処理をする新しいタスクを生成。
+ /// </summary>
+ /// <typeparam name="U">継続タスクの戻り値の型。</typeparam>
+ /// <param name="continuation">継続処理のタスク生成メソッド。</param>
+ /// <returns>新しいタスク。</returns>
+ public Task<U> ContinueWithTask<U>(Func<T, Task<U>> continuation)
+ {
+ return new ContinuationTask<U>(this, () =>
+ {
+ var res = this.Result;
+ return continuation(res);
+ });
+ }
+
+ /// <summary>
+ /// 1つのタスク完了後に、続けて処理をする新しいタスクを生成。
+ /// </summary>
+ /// <typeparam name="U">継続タスクの戻り値の型。</typeparam>
+ /// <param name="continuation">継続処理のコルーチン生成メソッド。</param>
+ /// <returns>新しいタスク。</returns>
+ public Task<U> ContinueWith<U>(Func<T, Action<U>, IEnumerator> continuation)
+ {
+ Func<T, Task<U>> f = x => new Task<U>(a => continuation(x, a));
+ return this.ContinueWithTask<U>(f);
+ }
+ }
+
+ /// <summary>
+ /// ContinuationTask のジェネリック版と非ジェネリック版でほとんど同じ処理なのに、継承とかで処理を使いまわせないのでやむなく別クラスに。
+ /// </summary>
+ internal class ContinuationTaskInternal
+ {
+ private Task _task;
+ private Func<Task> _continuation;
+ private Action<Exception> _addError;
+ private Action _complete;
+ internal Task LastTask { get; private set; }
+
+ internal ContinuationTaskInternal(Task firstTask, Func<Task> continuation, Action<Exception> addError, Action complete)
+ {
+ _task = firstTask;
+ _continuation = continuation;
+ _addError = addError;
+ _complete = complete;
+ LastTask = null;
+ }
+
+ public object Current
+ {
+ get
+ {
+ if (_task != null)
+ return _task.Current;
+ return null;
+ }
+ }
+
+ public bool MoveNext()
+ {
+ if (_task != null)
+ {
+ var hasNext = _task.MoveNext();
+
+ if (hasNext) return true;
+
+ if (_task.Error != null)
+ {
+ _addError(_task.Error);
+ Complete();
+ return false;
+ }
+
+ if (_continuation != null)
+ {
+ _task = _continuation();
+ _continuation = null;
+ return true;
+ }
+ else
+ {
+ Complete();
+ return false;
+ }
+ }
+
+ return false;
+ }
+
+ private void Complete()
+ {
+ LastTask = _task;
+ _complete();
+ _task = null;
+ _continuation = null;
+ }
+ }
+
+ /// <summary>
+ /// 継続処理用のタスク クラス。
+ /// </summary>
+ internal class ContinuationTask : Task
+ {
+ ContinuationTaskInternal _inner;
+
+ internal ContinuationTask(Task firstTask, Func<Task> continuation)
+ {
+ _inner = new ContinuationTaskInternal(firstTask, continuation, this.AddError, this.Complete);
+ }
+
+ public override object Current { get { return _inner.Current; } }
+
+ public override bool MoveNext()
+ {
+ return _inner.MoveNext();
+ }
+ }
+
+ /// <summary>
+ /// 継続処理用のタスク クラス。
+ /// ジェネリック版(戻り値あり)。
+ /// </summary>
+ internal class ContinuationTask<U> : Task<U>
+ {
+ ContinuationTaskInternal _inner;
+
+ internal ContinuationTask(Task firstTask, Func<Task<U>> continuation)
+ {
+ _inner = new ContinuationTaskInternal(firstTask, () => continuation(), this.AddError, this.Complete);
+ }
+
+ public override object Current { get { return _inner.Current; } }
+
+ public override bool MoveNext()
+ {
+ return _inner.MoveNext();
+ }
+
+ public override U Result
+ {
+ get
+ {
+ var t = _inner.LastTask as Task<U>;
+ if (t != null)
+ {
+ return t.Result;
+ }
+ else
+ {
+ return default(U);
+ }
+ }
+ }
+ }
+}
View
16 IteratorTasks/IProgress.cs
@@ -0,0 +1,16 @@
+
+namespace Aiming.IteratorTasks
+{
+ /// <summary>
+ /// 進捗報告用のインターフェイス。
+ /// </summary>
+ /// <typeparam name="T">進捗度合を表す型。</typeparam>
+ public interface IProgress<T>
+ {
+ /// <summary>
+ /// 現在の進捗状況をレポート。
+ /// </summary>
+ /// <param name="value">現在の進捗状況。</param>
+ void Report(T value);
+ }
+}
View
67 IteratorTasks/IteratorTasks.csproj
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{3AB99F84-7E6F-4B01-9F57-AE5722C56893}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>IteratorTasks</RootNamespace>
+ <AssemblyName>IteratorTasks</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <TargetFrameworkProfile />
+ <SccProjectName>SAK</SccProjectName>
+ <SccLocalPath>SAK</SccLocalPath>
+ <SccAuxPath>SAK</SccAuxPath>
+ <SccProvider>SAK</SccProvider>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="CancellationToken.cs" />
+ <Compile Include="CancellationTokenSource.cs" />
+ <Compile Include="ContinuationTask.cs" />
+ <Compile Include="IProgress.cs" />
+ <Compile Include="MultiTask.cs" />
+ <Compile Include="Progress.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="Task.cs" />
+ <Compile Include="TaskCanceledException.cs" />
+ <Compile Include="TaskScheduler.cs" />
+ <Compile Include="TaskStatus.cs" />
+ <Compile Include="Util.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
View
86 IteratorTasks/MultiTask.cs
@@ -0,0 +1,86 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Aiming.IteratorTasks
+{
+ /// <summary>
+ /// 複数の例外を束ねる例外クラス。
+ /// 並行動作してると、複数のタスク内で同時に例外が発生する可能性があるので。
+ /// </summary>
+ public class AggregateException : Exception
+ {
+ private List<Exception> _exceptions;
+ public IEnumerable<Exception> Exceptions { get { return _exceptions.ToArray(); } }
+
+ public AggregateException(List<Exception> exceptions)
+ {
+ _exceptions = exceptions;
+ }
+
+ public override string Message
+ {
+ get
+ {
+ var count = _exceptions.Count;
+
+ if (count == 1) return _exceptions[0].Message;
+ else if (count > 1) return string.Format("AggregateException: {0} errors", count);
+ else return base.Message;
+ }
+ }
+ }
+
+ // もともと、Util とか MultiTask みたいな名前の静的クラスに持たせようかと思っていたものの、
+ // 結局、Task クラス自身に並行動作用のメソッドを持たせることに。
+ public partial class Task
+ {
+ public static Task WhenAllTask(params Task[] routines)
+ {
+ return new Task(WhenAll(routines));
+ }
+
+ /// <summary>
+ /// 複数の Task を並行実行する。
+ /// </summary>
+ /// <remarks>
+ /// 1フレームにつき、1つの Task の MoveNext しか呼ばないので、
+ /// N 個の Task を並行実行すると、個々の MoveNext は N フレームに1回しか呼ばれない。
+ /// </remarks>
+ /// <param name="routines">同時に動かしたい Task。</param>
+ /// <returns>束ねたコルーチン。</returns>
+ public static IEnumerator WhenAll(params Task[] routines)
+ {
+ int successCount = 0;
+ var errors = new List<Exception>();
+
+ foreach (var r in routines)
+ {
+ r.OnComplete(t =>
+ {
+ if (t.Error == null) ++successCount;
+ else errors.AddRange(t.Error.Exceptions);
+ });
+ }
+
+ do
+ {
+ for (int i = 0; i < routines.Length; i++)
+ {
+ var r = routines[i];
+ if (r == null) continue;
+
+ if (r.MoveNext())
+ {
+ yield return r.Current;
+ }
+ else
+ routines[i] = null;
+ }
+ } while (routines.Any(x => x != null));
+
+ if (errors.Count != 0) throw new AggregateException(errors);
+ }
+ }
+}
View
26 IteratorTasks/Progress.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace Aiming.IteratorTasks
+{
+ /// <summary>
+ /// イベントを使って進捗報告を受け取るためのクラス。
+ /// </summary>
+ /// <typeparam name="T">進捗度合を表す型。</typeparam>
+ public class Progress<T> : IProgress<T>
+ {
+ public Progress() { }
+
+ public Progress(Action<T> onProgressChanged) { ProgressChanged += onProgressChanged; }
+
+ /// <summary>
+ /// 進捗状況が変化したときに起こすイベント。
+ /// </summary>
+ public event Action<T> ProgressChanged;
+
+ void IProgress<T>.Report(T value)
+ {
+ var d = ProgressChanged;
+ if (d != null) d(value);
+ }
+ }
+}
View
36 IteratorTasks/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// アセンブリに関する一般情報は以下の属性セットをとおして制御されます。
+// アセンブリに関連付けられている情報を変更するには、
+// これらの属性値を変更してください。
+[assembly: AssemblyTitle("Aiming.IteratorTasks")]
+[assembly: AssemblyDescription("Task Asynchrony Library for Iterator-based Coroutine")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Aiming")]
+[assembly: AssemblyProduct("IteratorTasks")]
+[assembly: AssemblyCopyright("Copyright © Aiming 2012")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// ComVisible を false に設定すると、その型はこのアセンブリ内で COM コンポーネントから
+// 参照不可能になります。COM からこのアセンブリ内の型にアクセスする場合は、
+// その型の ComVisible 属性を true に設定してください。
+[assembly: ComVisible(false)]
+
+// 次の GUID は、このプロジェクトが COM に公開される場合の、typelib の ID です
+[assembly: Guid("dd7396f2-3e4d-4ab2-9de8-4b9f9257538c")]
+
+// アセンブリのバージョン情報は、以下の 4 つの値で構成されています:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// すべての値を指定するか、下のように '*' を使ってビルドおよびリビジョン番号を
+// 既定値にすることができます:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
View
412 IteratorTasks/Task.cs
@@ -0,0 +1,412 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Aiming.IteratorTasks
+{
+ /// <summary>
+ /// .NET 4 の Task 的にコルーチンを実行するためのクラス。
+ /// 戻り値なし版。
+ /// </summary>
+ public partial class Task : IEnumerator, IDisposable
+ {
+ protected IEnumerator Routine { get; set; }
+
+ private AggregateException _error;
+
+ /// <summary>
+ /// タスク中で発生した例外。
+ /// </summary>
+ /// <remarks>
+ /// 並行動作で、複数のタスクで同時に例外が起きたりする場合があるので、AggregateException にしてある。
+ /// </remarks>
+ public AggregateException Error
+ {
+ get
+ {
+ var internalTask = Routine as Task;
+ if(internalTask != null && internalTask.Error != null)
+ {
+ AddError(internalTask.Error);
+ internalTask.ClearError();
+ }
+ return _error;
+ }
+ private set
+ {
+ _error = value;
+ }
+ }
+
+ private List<Exception> _errors;
+
+ /// <summary>
+ /// Task.Error を AggregateException にしたので、例外の追加は this.Error = exc; じゃなくて、この AddError を介して行う。
+ /// 引数に AggregateException を渡した場合は、子要素を抜き出して統合。
+ /// </summary>
+ /// <param name="exc">追加したい例外。</param>
+ protected void AddError(Exception exc)
+ {
+ if (_error == null)
+ {
+ _errors = new List<Exception>();
+ _error = new AggregateException(_errors);
+ }
+
+ var agg = exc as AggregateException;
+ if (agg != null)
+ {
+ foreach (var e in agg.Exceptions)
+ {
+ _errors.Add(e);
+ }
+ }
+ else
+ {
+ _errors.Add(exc);
+ }
+ }
+
+ protected void ClearError()
+ {
+ if (_error != null)
+ {
+ _errors = null;
+ _error = null;
+ }
+ }
+
+ #region IEnumerator
+
+ // Current と MoveNext も明示的実装して隠したかったけども、それやると StartCoroutine で動かなくなるみたい。
+
+ public virtual object Current
+ {
+ get
+ {
+ if (IsCanceled) return null;
+ return Routine == null ? null : Routine.Current;
+ }
+ }
+
+ public virtual bool MoveNext()
+ {
+ if (Status == TaskStatus.Created) Status = TaskStatus.Running;
+ if (Status != TaskStatus.Running) return false;
+ if (Routine == null) return false;
+
+ bool hasNext;
+
+ try
+ {
+ hasNext = Routine.MoveNext();
+ }
+ catch (Exception exc)
+ {
+ AddError(exc);
+ hasNext = false;
+ }
+
+ if (!hasNext)
+ {
+ Complete();
+ }
+ return hasNext;
+ }
+
+ public void Dispose()
+ {
+ var d = Routine as IDisposable;
+ if (d != null) d.Dispose();
+ Routine = null;
+ }
+
+ void IEnumerator.Reset()
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Start(TaskScheduler scheduler)
+ {
+ if (Status != TaskStatus.Created)
+ throw new InvalidOperationException();
+
+ scheduler.QueueTask(this);
+ }
+
+ public TaskStatus Status { get; private set; }
+
+ public bool IsDone { get { return IsCompleted || IsCanceled || IsFaulted; } }
+ public bool IsCompleted { get { return Status == TaskStatus.RanToCompletion; } }
+ public bool IsCanceled { get { return Status == TaskStatus.Canceled; } }
+ public bool IsFaulted { get { return Status == TaskStatus.Faulted; } }
+
+ protected void Complete()
+ {
+ if (Error != null)
+ Status = TaskStatus.Faulted;
+ else
+ Status = TaskStatus.RanToCompletion;
+
+ if (_callback.Count != 0)
+ {
+ foreach (var c in _callback)
+ {
+ Invoke(c);
+ }
+ }
+ _callback.Clear();
+ }
+
+ private void Invoke(Action<Task> c)
+ {
+ try
+ {
+ c(this);
+ }
+ catch (Exception exc)
+ {
+ AddError(exc);
+ }
+ }
+
+ #endregion
+
+ List<Action<Task>> _callback = new List<Action<Task>>();
+
+ /// <summary>
+ /// コルーチン完了後に呼ばれるコールバックを登録。
+ /// </summary>
+ /// <param name="callback">コールバック。</param>
+ /// <returns>自分自身(fluent interface)。</returns>
+ /// <remarks>
+ /// ちなみに、callback 内で発生した例外も Error に統合されて、後段のタスクが実行されなくなる。
+ /// その意味で、OnComplete というよりは、HookComplete (完了に割って入る)の方が正しい気も。
+ /// </remarks>
+ public Task OnComplete(Action<Task> callback)
+ {
+ if (IsDone)
+ Invoke(callback);
+ else
+ _callback.Add(callback);
+
+ return this;
+ }
+
+ /// <summary>
+ /// コルーチンがエラー終了(例外発生)した時だけ呼ばれるコールバックを登録。
+ /// </summary>
+ /// <param name="errorHandler">エラー処理コールバック。</param>
+ /// <returns>自分自身(fluent interface)。</returns>
+ /// <remarks>
+ /// 内部的には OnComplete を呼んでるので、挙動はそちらに準じる。
+ /// </remarks>
+ public Task OnError<T>(Action<T> errorHandler)
+ where T : Exception
+ {
+ return this.OnComplete(t =>
+ {
+ if (t.Error != null)
+ foreach (var e in t.Error.Exceptions)
+ {
+ var et = e as T;
+ if (et != null)
+ errorHandler(et);
+ }
+ });
+ }
+
+ /// <remarks>
+ /// OnError、最初から IEnumerable{T} 受け取るようにしとけばよかった…
+ /// 後からオーバーロード足そうとしたら、既存コードがエラーになったので、しょうがなく別メソッドに。
+ /// AsOne ってのも微妙だけども、2 とか Ex とかつけるよりはマシなので。
+ /// </remarks>
+ public Task OnErrorAsOne(Action<IEnumerable<Exception>> errorHandler)
+ {
+ return this.OnComplete(t =>
+ {
+ if (t.Error != null)
+ errorHandler(t.Error.Exceptions);
+ });
+ }
+
+ /// <summary>
+ /// タスクをキャンセルします。
+ /// </summary>
+ public void Cancel()
+ {
+ if (Cancellation == null)
+ throw new InvalidOperationException("Can't cancel Task.");
+
+ Cancellation.Cancel();
+
+ MoveNext();
+ }
+
+ public void Cancel(Exception e)
+ {
+ if (Cancellation == null)
+ throw new InvalidOperationException("Can't cancel Task.");
+
+ Cancellation.Cancel(e);
+
+ MoveNext();
+ }
+
+ public void ForceCancel()
+ {
+ ForceCancel(new TaskCanceledException("Task force canceled."));
+ }
+
+ /// <summary>
+ /// タスクを強制キャンセルします。OnCompleteは呼ばれません。
+ /// </summary>
+ public void ForceCancel(Exception e)
+ {
+ Status = TaskStatus.Canceled;
+ AddError(e);
+ this.Dispose();
+ }
+
+ protected Task()
+ {
+ }
+
+ /// <summary>
+ /// 最初から完了済みのタスクを生成。
+ /// Empty とか Return 用。
+ /// </summary>
+ /// <param name="completed"></param>
+ protected Task(TaskStatus status)
+ {
+ Status = status;
+ }
+
+ /// <summary>
+ /// コルーチンを与えて初期化。
+ /// </summary>
+ /// <param name="routine">コルーチン。</param>
+ public Task(IEnumerator routine) { Routine = routine; }
+
+ /// <summary>
+ /// コルーチン生成メソッドを与えて初期化。
+ /// </summary>
+ /// <param name="starter"></param>
+ public Task(Func<IEnumerator> starter)
+ {
+ Routine = starter();
+ }
+
+ public CancellationTokenSource Cancellation { get; set; }
+
+ /// <summary>
+ /// タスクが Error を持っていたらそれを throw。
+ /// </summary>
+ /// <returns>自分自身(fluent interface)。</returns>
+ private Task Check()
+ {
+ if (_error != null)
+ {
+ throw _error;
+ }
+ return this;
+ }
+
+ /// <summary>
+ /// 空タスク(作った時点で完了済み)を生成。
+ /// </summary>
+ /// <returns>空タスク。</returns>
+ public static Task Empty()
+ {
+ return _empty;
+ }
+
+ private readonly static Task _empty = new Task(TaskStatus.RanToCompletion);
+
+ /// <summary>
+ /// 単に値を返す(作った時点で完了済み、最初から Return の値を持つ)タスクを生成。
+ /// </summary>
+ /// <typeparam name="T">戻り値の型。</typeparam>
+ /// <param name="value">返したい値。</param>
+ /// <returns>完了済みタスク。</returns>
+ public static Task<T> Return<T>(T value)
+ {
+ return new Task<T>(value);
+ }
+
+ public Task<U> Select<U>(Func<U> selector)
+ {
+ return this.ContinueWithTask<U>(() => Task.Return(selector()));
+ }
+ }
+
+ /// <summary>
+ /// .NET 4 の Task 的にコルーチンを実行するためのクラス。
+ /// 戻り値あり版。
+ /// </summary>
+ /// <typeparam name="T">最終的に返す型。</typeparam>
+ public partial class Task<T> : Task
+ {
+ /// <summary>
+ /// 最終的に返したい値。
+ /// </summary>
+ virtual public T Result
+ {
+ get
+ {
+ if (Error != null) throw Error;
+ return _result;
+ }
+ }
+ private T _result;
+
+ internal Task() { }
+
+ /// <summary>
+ /// Task.Return 用。
+ /// </summary>
+ /// <param name="result"></param>
+ internal Task(T result) : base(TaskStatus.RanToCompletion)
+ {
+ _result = result;
+ }
+
+ /// <summary>
+ /// コルーチン生成メソッドを与えて初期化。
+ /// </summary>
+ /// <param name="starter"></param>
+ public Task(Func<Action<T>, IEnumerator> starter)
+ {
+ Routine = starter(r => { _result = r; });
+ }
+
+ /// <summary>
+ /// コルーチン完了後に呼ばれるコールバックを登録。
+ /// </summary>
+ /// <param name="callback">コールバック。</param>
+ /// <returns>自分自身(fluent interface)。</returns>
+ public Task<T> OnComplete(Action<Task<T>> callback)
+ {
+ base.OnComplete(t => callback(this));
+ return this;
+ }
+
+ /// <summary>
+ /// Task.Check と同様。
+ /// </summary>
+ /// <returns>自分自身(fluent interface)。</returns>
+ private Task<T> Check()
+ {
+ if (Error != null)
+ {
+ throw Error;
+ }
+ return this;
+ }
+
+ public Task<U> Select<U>(Func<T, U> selector)
+ {
+ return this.ContinueWithTask<U>(x => Task.Return(selector(x)));
+ }
+ }
+}
View
11 IteratorTasks/TaskCanceledException.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace Aiming.IteratorTasks
+{
+ public class TaskCanceledException : OperationCanceledException
+ {
+ public TaskCanceledException() { }
+ public TaskCanceledException(string message) : base(message) { }
+ public TaskCanceledException(string message, Exception innerException) : base(message, innerException) { }
+ }
+}
View
12 IteratorTasks/TaskScheduler.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Aiming.IteratorTasks
+{
+ public abstract class TaskScheduler
+ {
+ protected internal abstract void QueueTask(Task task);
+ }
+}
View
31 IteratorTasks/TaskStatus.cs
@@ -0,0 +1,31 @@
+
+namespace Aiming.IteratorTasks
+{
+ public enum TaskStatus
+ {
+ /// <summary>
+ /// 実行前。
+ /// </summary>
+ Created,
+
+ /// <summary>
+ /// 実行中。
+ /// </summary>
+ Running,
+
+ /// <summary>
+ /// 正常終了。
+ /// </summary>
+ RanToCompletion,
+
+ /// <summary>
+ /// キャンセルされた。
+ /// </summary>
+ Canceled,
+
+ /// <summary>
+ /// 例外が出て止まった。
+ /// </summary>
+ Faulted,
+ }
+}
View
35 IteratorTasks/Util.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections;
+
+namespace Aiming.IteratorTasks
+{
+ public static class Util
+ {
+ /// <summary>
+ /// Unity の StartCoroutine を呼ぶ際に、null を渡しても平気かどうかわからなかったので、ダミーの 0 要素の IEnumeartor を返す。
+ /// 要検証かも。
+ /// </summary>
+ /// <returns></returns>
+ public static IEnumerator EmptyRoutine()
+ {
+ return new object[0].GetEnumerator();
+ }
+
+ public static IEnumerator Concat(Func<IEnumerator> e1, Func<IEnumerator> e2)
+ {
+ var x1 = e1();
+ while (x1.MoveNext()) yield return x1.Current;
+ Dispose(x1);
+
+ var x2 = e2();
+ while (x2.MoveNext()) yield return x2.Current;
+ Dispose(x2);
+ }
+
+ public static void Dispose(object obj)
+ {
+ var d = obj as IDisposable;
+ if (d != null) d.Dispose();
+ }
+ }
+}
View
36 SampleTaskRunner/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// アセンブリに関する一般情報は以下の属性セットをとおして制御されます。
+// アセンブリに関連付けられている情報を変更するには、
+// これらの属性値を変更してください。
+[assembly: AssemblyTitle("SampleTaskRunner")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("SampleTaskRunner")]
+[assembly: AssemblyCopyright("Copyright © 2012")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// ComVisible を false に設定すると、その型はこのアセンブリ内で COM コンポーネントから
+// 参照不可能になります。COM からこのアセンブリ内の型にアクセスする場合は、
+// その型の ComVisible 属性を true に設定してください。
+[assembly: ComVisible(false)]
+
+// 次の GUID は、このプロジェクトが COM に公開される場合の、typelib の ID です
+[assembly: Guid("22390451-5763-472c-aad7-0461392ed9c6")]
+
+// アセンブリのバージョン情報は、以下の 4 つの値で構成されています:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// すべての値を指定するか、下のように '*' を使ってビルドおよびリビジョン番号を
+// 既定値にすることができます:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
View
63 SampleTaskRunner/SampleTaskRunner.csproj
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{5BF3A7F6-634B-4B1E-B3B2-134ECF8B1B26}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>SampleTaskRunner</RootNamespace>
+ <AssemblyName>SampleTaskRunner</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <TargetFrameworkProfile />
+ <SccProjectName>SAK</SccProjectName>
+ <SccLocalPath>SAK</SccLocalPath>
+ <SccAuxPath>SAK</SccAuxPath>
+ <SccProvider>SAK</SccProvider>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="TaskRunner.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\IteratorTasks\IteratorTasks.csproj">
+ <Project>{3ab99f84-7e6f-4b01-9f57-ae5722c56893}</Project>
+ <Name>IteratorTasks</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
View
46 SampleTaskRunner/TaskRunner.cs
@@ -0,0 +1,46 @@
+using System.Collections.Generic;
+using Aiming.IteratorTasks;
+
+namespace SampleTaskRunner
+{
+ public class TaskRunner : TaskScheduler
+ {
+ private List<Task> _runningTasks = new List<Task>();
+ private List<Task> _toBeRemoved = new List<Task>();
+
+ protected override void QueueTask(Task task)
+ {
+ _runningTasks.Add(task);
+ }
+
+ /// <summary>
+ /// 1フレーム処理を進める。
+ /// </summary>
+ public void Update()
+ {
+ foreach (var t in _runningTasks)
+ {
+ if (!t.MoveNext())
+ _toBeRemoved.Add(t);
+ }
+
+ foreach (var t in _toBeRemoved)
+ {
+ _runningTasks.Remove(t);
+ }
+ _toBeRemoved.Clear();
+ }
+
+ /// <summary>
+ /// 与えたフレーム数分、処理を進める。
+ /// </summary>
+ /// <param name="numFrames">進めたいフレーム数。</param>
+ public void Update(int numFrames)
+ {
+ for (int i = 0; i < numFrames; i++)
+ {
+ this.Update();
+ }
+ }
+ }
+}
View
146 TestIteratorTasks/CancellationTokenTest.cs
@@ -0,0 +1,146 @@
+using System;
+using System.Text;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Aiming.IteratorTasks;
+
+namespace TestIteratorTasks
+{
+ [TestClass]
+ public class CancellationTokenTest
+ {
+ [TestMethod]
+ public void キャンセルトークンを渡しても_Cancelを呼ばなければ正常終了()
+ {
+ var x = 10;
+ var runner = new SampleTaskRunner.TaskRunner();
+
+ var t = new Task<double>(c => Coroutines.F1Cancelable(x, 20, c, CancellationToken.None));
+
+ t.Start(runner);
+ while (!t.IsCompleted)
+ runner.Update();
+
+ Assert.AreEqual(Coroutines.F1(x), t.Result);
+ }
+
+ [TestMethod]
+ public void キャンセルしたときにOperationCanceld例外発生()
+ {
+ var x = 10;
+ var runner = new SampleTaskRunner.TaskRunner();
+
+ var cts = new CancellationTokenSource();
+ var t = new Task<double>(c => Coroutines.F1Cancelable(x, 20, c, cts.Token));
+
+ t.Start(runner);
+ runner.Update(5);
+ cts.Cancel();
+
+ // 次の1回の実行でタスクが終わるはず
+ runner.Update();
+
+ // この場合は IsCanceled にならない
+ Assert.IsTrue(t.IsFaulted);
+ Assert.AreEqual(typeof(TaskCanceledException), t.Error.Exceptions.Single().GetType());
+ }
+
+ [TestMethod]
+ public void TaskのForceCancelで強制的にタスクを止めたときはOnCompleteも呼ばれない()
+ {
+ var x = 10;
+ var runner = new SampleTaskRunner.TaskRunner();
+
+ var cts = new CancellationTokenSource();
+ var t = new Task<double>(c => Coroutines.F1Cancelable(x, 20, c, cts.Token));
+
+ t.OnComplete(_ =>
+ {
+ Assert.Fail();
+ });
+
+ t.Start(runner);
+ runner.Update(5);
+ t.ForceCancel();
+
+ runner.Update();
+
+ // この場合は IsCanceled に
+ Assert.IsTrue(t.IsCanceled);
+ }
+
+ [TestMethod]
+ public void TaskにCancellationTokenSourceを渡しておいて_TaskのCancelメソッド経由でキャンセルできる()
+ {
+ var x = 10;
+ var runner = new SampleTaskRunner.TaskRunner();
+
+ var cts = new CancellationTokenSource();
+ var t = new Task<double>(c => Coroutines.F1Cancelable(x, 20, c, cts.Token));
+
+ t.Cancellation = cts;
+
+ t.Start(runner);
+ runner.Update(5);
+ t.Cancel(); // Task.Cancel の中で1度 MoveNext して、即座にキャンセル処理が動くようにする
+
+ // 挙動自体は cts.Cancel(); と同じ
+ Assert.IsTrue(t.IsFaulted);
+ Assert.AreEqual(typeof(TaskCanceledException), t.Error.Exceptions.Single().GetType());
+ }
+
+ [TestMethod]
+ public void Cancel時にRegisterで登録したデリゲートが呼ばれる()
+ {
+ var runner = new SampleTaskRunner.TaskRunner();
+
+ {
+ // キャンセルしない場合
+ var cts = new CancellationTokenSource();
+ var t = new Task<string>(c => Cancelで戻り値が切り替わるコルーチン(10, c, cts.Token));
+ t.Start(runner);
+ runner.Update(20);
+
+ Assert.IsTrue(t.IsCompleted);
+ Assert.AreEqual(CompletedMessage, t.Result);
+ }
+
+ {
+ // キャンセルする場合
+ var cts = new CancellationTokenSource();
+ var t = new Task<string>(c => Cancelで戻り値が切り替わるコルーチン(10, c, cts.Token));
+ t.Start(runner);
+ runner.Update(5);
+ cts.Cancel();
+ runner.Update(5);
+
+ Assert.IsTrue(t.IsCompleted);
+ Assert.AreEqual(CanceledMessage, t.Result);
+ }
+ }
+
+ const string CompletedMessage = "最後まで実行された時の戻り値";
+ const string CanceledMessage = "キャンセルされた時の戻り値";
+
+ static System.Collections.IEnumerator Cancelで戻り値が切り替わるコルーチン(int n, Action<string> completed, CancellationToken ct)
+ {
+ var message = CompletedMessage;
+
+ ct.Register(() =>
+ {
+ message = CanceledMessage;
+ });
+
+ for (int i = 0; i < n; i++)
+ {
+ if (ct.IsCancellationRequested)
+ break;
+
+ yield return null;
+ }
+
+ completed(message);
+ }
+ }
+}
View
152 TestIteratorTasks/Coroutines.cs
@@ -0,0 +1,152 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Aiming.IteratorTasks;
+
+namespace TestIteratorTasks
+{
+ /// <summary>
+ /// テスト用に使うコルーチンいろいろ。
+ /// </summary>
+ class Coroutines
+ {
+ #region 同期処理
+
+ // 中身には深い意味なし。
+ // 単に、F3(F2(F1(x))) みたいに繋ぎたいだけ。
+
+ public static double F1(double x)
+ {
+ return x * x;
+ }
+
+ public static string F2(double x)
+ {
+ return x.ToString();
+ }
+
+ public static int F3(string s)
+ {
+ return s.Length;
+ }
+
+ public static int FError()
+ {
+ throw new NotSupportedException();
+ }
+
+ #endregion
+ #region イテレーター非同期処理
+
+ public static Task NFrameTask(int n)
+ {
+ return new Task(() => NFrame(n));
+ }
+
+ public static System.Collections.IEnumerator NFrame(int n)
+ {
+ for (int i = 0; i < n; i++)
+ {
+ yield return null;
+ }
+ }
+
+ /// <summary>
+ /// 5フレーム後にF1の結果を返す。
+ /// </summary>
+ public static System.Collections.IEnumerator F1Async(double x, Action<double> completed)
+ {
+ for (int i = 0; i < 5; i++)
+ {
+ yield return null;
+ }
+
+ var result = F1(x);
+ completed(result);
+ }
+
+ /// <summary>
+ /// 5フレーム後にF2の結果を返す。
+ /// </summary>
+ public static System.Collections.IEnumerator F2Async(double x, Action<string> completed)
+ {
+ for (int i = 0; i < 5; i++)
+ {
+ yield return null;
+ }
+
+ var result = F2(x);
+ completed(result);
+ }
+
+ /// <summary>
+ /// 5フレーム後にF3の結果を返す。
+ /// </summary>
+ public static System.Collections.IEnumerator F3Async(string s, Action<int> completed)
+ {
+ for (int i = 0; i < 5; i++)
+ {
+ yield return null;
+ }
+
+ var result = F3(s);
+ completed(result);
+ }
+
+ /// <summary>
+ /// 5フレーム後にNotSupportedExceptionを出す。
+ /// </summary>
+ public static System.Collections.IEnumerator FErrorAsync()
+ {
+ for (int i = 0; i < 5; i++)
+ {
+ yield return null;
+ }
+
+ FError();
+ }
+
+ /// <summary>
+ /// 5フレーム後にNotSupportedExceptionを出す。
+ /// </summary>
+ public static System.Collections.IEnumerator FErrorAsync(Action<int> campleted)
+ {
+ for (int i = 0; i < 5; i++)
+ {
+ yield return null;
+ }
+
+ var result = FError();
+ campleted(result);
+ }
+
+ #endregion
+ #region キャンセル機能付き
+
+ /// <summary>
+ /// n フレーム後に F1(x)を返すコルーチン。
+ /// キャンセル機能付き。
+ /// </summary>
+ /// <param name="x">入力値。</param>
+ /// <param name="n">コルーチン稼働のフレーム数。</param>
+ /// <param name="completed">完了時に呼ばれるデリゲート。</param>
+ /// <param name="ct">キャンセル用トークン。</param>
+ /// <returns></returns>
+ public static System.Collections.IEnumerator F1Cancelable(double x, int n, Action<double> completed, CancellationToken ct)
+ {
+ for (int i = 0; i < n; i++)
+ {
+ // キャンセルへの対処はあくまでコルーチン側の債務
+ // 例外を出して止める。
+ ct.ThrowIfCancellationRequested();
+
+ yield return null;
+ }
+
+ completed(F1(x));
+ }
+
+ #endregion
+ }
+}
View
49 TestIteratorTasks/ProgressTest.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Text;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Aiming.IteratorTasks;
+
+namespace TestIteratorTasks
+{
+ [TestClass]
+ public class ProgressTest
+ {
+ [TestMethod]
+ public void Progressでは_Reportが呼ばれるたびにProgressChangedイベントが起こる()
+ {
+ var progress = new Progress<int>();
+ var reportedItems = new List<int>();
+
+ progress.ProgressChanged += i =>
+ {
+ reportedItems.Add(i);
+ };
+
+ var t = new Task<int>(c => 進捗報告付きのコルーチン(c, progress));
+
+ var runner = new SampleTaskRunner.TaskRunner();
+ t.Start(runner);
+
+ for (int i = 0; i < 100; i++)
+ {
+ Assert.AreEqual(i, reportedItems.Count);
+ runner.Update();
+ Assert.AreEqual(i + 1, reportedItems.Count);
+ Assert.AreEqual(i, reportedItems.Last());
+ }
+ }
+
+ static System.Collections.IEnumerator 進捗報告付きのコルーチン(Action<int> completed, IProgress<int> progress)
+ {
+ for (int i = 0; i < 100; i++)
+ {
+ progress.Report(i);
+ yield return null;
+ }
+
+ completed(100);
+ }
+ }
+}
View
36 TestIteratorTasks/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// アセンブリに関する一般情報は以下の属性セットをとおして制御されます。
+// アセンブリに関連付けられている情報を変更するには、
+// これらの属性値を変更してください。
+[assembly: AssemblyTitle("TestIteratorTasks")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("TestIteratorTasks")]
+[assembly: AssemblyCopyright("Copyright © 2012")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// ComVisible を false に設定すると、その型はこのアセンブリ内で COM コンポーネントから
+// 参照できなくなります。このアセンブリ内で COM から型にアクセスする必要がある場合は、
+// その型の ComVisible 属性を true に設定してください。
+[assembly: ComVisible(false)]
+
+// このプロジェクトが COM に公開される場合、次の GUID がタイプ ライブラリの ID になります。
+[assembly: Guid("f0c69c3f-db5e-4317-9963-3ea28ab28783")]
+
+// アセンブリのバージョン情報は、以下の 4 つの値で構成されています:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// すべての値を指定するか、以下のように '*' を使用してビルド番号とリビジョン番号を
+// 既定値にすることができます:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
View
314 TestIteratorTasks/TaskTest.cs
@@ -0,0 +1,314 @@
+using System;
+using System.Text;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Aiming.IteratorTasks;
+
+namespace TestIteratorTasks
+{
+ [TestClass]
+ public class TaskTest
+ {
+
+ [TestMethod]
+ public void Nフレーム実行するイテレーターが丁度NフレームIsCompleted_falseになってることを確認()
+ {
+ const int N = 50;
+
+ var runnner = new SampleTaskRunner.TaskRunner();
+ var task = Coroutines.NFrameTask(N);
+ task.Start(runnner);
+
+ for (int i = 0; i < 2 * N; i++)
+ {
+ runnner.Update();
+
+ if (i < N)
+ Assert.IsFalse(task.IsCompleted);
+ else
+ Assert.IsTrue(task.IsCompleted);
+ }
+ }
+
+ [TestMethod]
+ public void Task_Tで正常終了するとResultに結果が入る()
+ {
+ var x = 10;
+ var y = Coroutines.F1(x);
+
+ var task = new Task<double>(c => Coroutines.F1Async(x, c))
+ .OnComplete(t => Assert.AreEqual(t.Result, y));
+
+ var runnner = new SampleTaskRunner.TaskRunner();
+ task.Start(runnner);
+ runnner.Update(10);
+
+ Assert.AreEqual(y, task.Result);
+ }
+
+ [TestMethod]
+ public void 一度完了したタスク_何度でも結果が取れる()
+ {
+ var x = 10;
+ var y = Coroutines.F1(x);
+
+ var task = new Task<double>(c => Coroutines.F1Async(x, c));
+
+ var runnner = new SampleTaskRunner.TaskRunner();
+ task.Start(runnner);
+ runnner.Update(10);
+
+ Assert.AreEqual(y, task.Result);
+ Assert.AreEqual(y, task.Result);
+ Assert.AreEqual(y, task.Result);
+ Assert.AreEqual(y, task.Result);
+ }
+
+ [TestMethod]
+ public void タスク完了時にOnCompleteが呼ばれる()
+ {
+ var runnner = new SampleTaskRunner.TaskRunner();
+
+ var x = 10;
+ var y = Coroutines.F1(x);
+
+ bool called = false;
+
+ var task = new Task<double>(c => Coroutines.F1Async(x, c));
+ task.OnComplete(t => called = true);
+ task.Start(runnner);
+
+ Assert.IsFalse(called);
+
+ runnner.Update(10);
+
+ Assert.IsTrue(called);
+ }
+
+ [TestMethod]
+ public void 完了済みのタスクでOnCompleteすると_即座にコールバックが呼ばれる()
+ {
+ var runnner = new SampleTaskRunner.TaskRunner();
+
+ var x = 10;
+ var y = Coroutines.F1(x);
+
+ var task = new Task<double>(c => Coroutines.F1Async(x, c));
+ task.Start(runnner);
+ runnner.Update(10);
+
+ Assert.IsTrue(task.IsCompleted);
+
+ bool called = false;
+
+ task.OnComplete(t => called = true);
+
+ Assert.IsTrue(called);
+ }
+
+ [TestMethod]
+ public void 開始前_実行中_正常終了_エラー終了_キャンセルされた_がわかる()
+ {
+ var x = 10.0;
+ var task = new Task<double>(c => Coroutines.F1Async(x, c));
+
+ Assert.AreEqual(TaskStatus.Created, task.Status);
+
+ var runnner = new SampleTaskRunner.TaskRunner();
+ task.Start(runnner);
+
+ runnner.Update();
+ Assert.AreEqual(TaskStatus.Running, task.Status);
+
+ runnner.Update(10);
+ Assert.AreEqual(TaskStatus.RanToCompletion, task.Status);
+
+ var errorTask = new Task(Coroutines.FErrorAsync);
+ Assert.AreEqual(TaskStatus.Created, errorTask.Status);
+
+ errorTask.Start(runnner);
+ runnner.Update();
+ Assert.AreEqual(TaskStatus.Running, errorTask.Status);
+
+ runnner.Update(10);
+ Assert.AreEqual(TaskStatus.Faulted, errorTask.Status);
+ }
+
+ [TestMethod]
+ public void 実行途中のタスクを再スタートしようとしたら例外を出す()
+ {
+ var x = 10.0;
+ var task = new Task<double>(c => Coroutines.F1Async(x, c));
+
+ Assert.AreEqual(TaskStatus.Created, task.Status);
+
+ var runnner = new SampleTaskRunner.TaskRunner();
+ task.Start(runnner);
+
+ runnner.Update();
+ Assert.AreEqual(TaskStatus.Running, task.Status);
+
+ try
+ {
+ task.Start(runnner);
+ }
+ catch (InvalidOperationException)
+ {
+ return;
+ }
+
+ Assert.Fail();
+ }
+
+ [TestMethod]
+ public void タスク中で例外が出たらErrorプロパティに例外が入る()
+ {
+ var task = new Task<int>(Coroutines.FErrorAsync)
+ .OnComplete(t =>
+ {
+ Assert.IsTrue(t.IsFaulted);
+ Assert.IsNotNull(t.Error);
+ })
+ .ContinueWith(呼ばれてはいけない);
+
+ var runner = new SampleTaskRunner.TaskRunner();
+ task.Start(runner);
+ runner.Update(20);
+ }
+
+ [TestMethod]
+ public void タスク中で例外が出たらOnErrorが呼ばれる_特定の型の例外だけ拾う()
+ {
+ var notSupportedCalled = false;
+ var outOfRangeCalled = false;
+
+ var task = new Task(Coroutines.FErrorAsync)
+ .OnError<NotSupportedException>(e => notSupportedCalled = true)
+ .OnError<IndexOutOfRangeException>(e => outOfRangeCalled = true);
+
+ var runner = new SampleTaskRunner.TaskRunner();
+ task.Start(runner);
+ runner.Update(20);
+
+ Assert.IsTrue(notSupportedCalled);
+ Assert.IsFalse(outOfRangeCalled);
+ }
+
+ [TestMethod]
+ public void タスク中で例外が出たときにResultをとろうとすると例外再スロー()
+ {
+ var task = new Task<int>(Coroutines.FErrorAsync)
+ .OnComplete(t =>
+ {
+ Assert.IsTrue(t.IsFaulted);
+
+ try
+ {
+ var result = t.Result;
+ }
+ catch
+ {
+ return;
+ }
+ Assert.Fail();
+ })
+ .ContinueWith(呼ばれてはいけない);
+
+ var runner = new SampleTaskRunner.TaskRunner();
+ task.Start(runner);
+ runner.Update(20);
+ }
+
+ [TestMethod]
+ public void ContinueWithで継続処理を実行できる()
+ {
+ var x = 10.0;
+ var x1 = Coroutines.F1(x);
+ var x2 = Coroutines.F2(x1);
+ var x3 = Coroutines.F3(x2);
+
+ var task = new Task<double>(c => Coroutines.F1Async(x, c))
+ .OnComplete(t => Assert.AreEqual(t.Result, x1))
+ .ContinueWith<string>(Coroutines.F2Async)
+ .OnComplete(t => Assert.AreEqual(t.Result, x2))
+ .ContinueWith<int>(Coroutines.F3Async)
+ .OnComplete(t => Assert.AreEqual(t.Result, x2))
+ ;
+
+ var runner = new SampleTaskRunner.TaskRunner();
+ task.Start(runner);
+
+ runner.Update(20);
+ }
+
+ [TestMethod]
+ public void ContinueWithは前段が正常終了したときにだけ呼ばれる()
+ {
+ var onCompletedCalled = false;
+
+ var task = new Task(Coroutines.FErrorAsync)
+ .OnComplete(t =>
+ {
+ Assert.IsTrue(t.IsFaulted);
+ onCompletedCalled = true;
+ })
+ .ContinueWith(呼ばれてはいけない);
+
+ var runner = new SampleTaskRunner.TaskRunner();
+ task.Start(runner);
+ runner.Update(20);
+
+ Assert.IsTrue(onCompletedCalled);
+ }
+
+ private System.Collections.IEnumerator 呼ばれてはいけない()
+ {
+ Assert.Fail();
+ yield return null;
+ }
+
+ [TestMethod]
+ public void OnCompleteは_直前のタスク完了時_エラーも正常終了も_どちらも呼ばれる()
+ {
+ var errorTaskCalled = false;
+ var normalTaskCalled = false;
+
+ var normalTask = new Task(() => Coroutines.NFrame(5))
+ .OnComplete(t => normalTaskCalled = true);
+ var errorTask = new Task<int>(Coroutines.FErrorAsync)
+ .OnComplete(t => errorTaskCalled = true);
+
+ var runner = new SampleTaskRunner.TaskRunner();
+ errorTask.Start(runner);
+ normalTask.Start(runner);
+ runner.Update(20);
+
+ Assert.IsTrue(normalTaskCalled);
+ Assert.IsTrue(errorTaskCalled);
+ }
+
+ [TestMethod]
+ public void WhenAllでタスクの並行動作できる()
+ {
+ var t1 = new Task(() => Coroutines.NFrame(3));
+ var t2 = new Task(() => Coroutines.NFrame(5));
+ var t3 = new Task(() => Coroutines.NFrame(7));
+
+ var task = Task.WhenAllTask(t1, t2, t3)
+ .OnComplete(t =>
+ {
+ Assert.IsTrue(t1.IsCompleted);
+ Assert.IsTrue(t2.IsCompleted);
+ Assert.IsTrue(t3.IsCompleted);
+ });
+
+ var runner = new SampleTaskRunner.TaskRunner();
+ task.Start(runner);
+
+ runner.Update(20);
+
+ Assert.IsTrue(task.IsCompleted);
+ }
+ }
+}
View
101 TestIteratorTasks/TestIteratorTasks.csproj
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform