diff --git a/Implab.Fx.Test/Implab.Fx.Test.csproj b/Implab.Fx.Test/Implab.Fx.Test.csproj --- a/Implab.Fx.Test/Implab.Fx.Test.csproj +++ b/Implab.Fx.Test/Implab.Fx.Test.csproj @@ -31,6 +31,23 @@ prompt 4 + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + diff --git a/Implab.Fx/Implab.Fx.csproj b/Implab.Fx/Implab.Fx.csproj --- a/Implab.Fx/Implab.Fx.csproj +++ b/Implab.Fx/Implab.Fx.csproj @@ -30,6 +30,23 @@ prompt 4 + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + diff --git a/Implab.Test/Implab.Test.csproj b/Implab.Test/Implab.Test.csproj --- a/Implab.Test/Implab.Test.csproj +++ b/Implab.Test/Implab.Test.csproj @@ -31,6 +31,23 @@ prompt 4 + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + diff --git a/Implab.sln b/Implab.sln --- a/Implab.sln +++ b/Implab.sln @@ -20,22 +20,40 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU + Debug 4.5|Any CPU = Debug 4.5|Any CPU + Release 4.5|Any CPU = Release 4.5|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU + {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU + {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release|Any CPU.ActiveCfg = Release|Any CPU {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release|Any CPU.Build.0 = Release|Any CPU + {2F31E405-E267-4195-A05D-574093C21209}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU + {2F31E405-E267-4195-A05D-574093C21209}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU {2F31E405-E267-4195-A05D-574093C21209}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2F31E405-E267-4195-A05D-574093C21209}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2F31E405-E267-4195-A05D-574093C21209}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU + {2F31E405-E267-4195-A05D-574093C21209}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU {2F31E405-E267-4195-A05D-574093C21209}.Release|Any CPU.ActiveCfg = Release|Any CPU {2F31E405-E267-4195-A05D-574093C21209}.Release|Any CPU.Build.0 = Release|Any CPU + {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU + {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU + {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.ActiveCfg = Release|Any CPU {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.Build.0 = Release|Any CPU + {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU + {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU + {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.ActiveCfg = Release|Any CPU {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection diff --git a/Implab/IPromise.cs b/Implab/IPromise.cs --- a/Implab/IPromise.cs +++ b/Implab/IPromise.cs @@ -27,16 +27,26 @@ namespace Implab { /// bool IsCancelled { get; } - IPromise Then(Action success,ErrorHandler error); + IPromise Then(Action success, ErrorHandler error, Action cancel); + IPromise Then(Action success, ErrorHandler error); IPromise Then(Action success); + + /// + /// Добавляет последнй обработчик в цепочку обещаний, не создает промежуточных обещаний. + /// + /// Success. + /// Error. + /// Cancel. + void Last(Action success, ErrorHandler error, Action cancel); + void Last(Action success, ErrorHandler error); + void Last(Action success); + IPromise Error(ErrorHandler error); /// /// Обрабатывает либо ошибку, либо результат. Событие отмены не обрабатывается. /// /// Обработчик. /// После обработке ошибки, она передается дальше. - IPromise Anyway(Action handler); - /// /// Обрабатывает либо ошибку, либо результат, либо отмену обещания. /// diff --git a/Implab/IPromiseT.cs b/Implab/IPromiseT.cs --- a/Implab/IPromiseT.cs +++ b/Implab/IPromiseT.cs @@ -3,27 +3,35 @@ using System.Collections.Generic; using System.Linq; using System.Text; -namespace Implab -{ - public interface IPromise: IPromise - { +namespace Implab { + public interface IPromise: IPromise { new T Join(); + new T Join(int timeout); + IPromise Then(ResultHandler success, ErrorHandler error, Action cancel); + IPromise Then(ResultHandler success, ErrorHandler error); + IPromise Then(ResultHandler success); + + void Last(ResultHandler success, ErrorHandler error, Action cancel); + void Last(ResultHandler success, ErrorHandler error); + void Last(ResultHandler success); + IPromise Error(ErrorHandler error); - IPromise Map(ResultMapper mapper, ErrorHandler error); - IPromise Map(ResultMapper mapper); + IPromise Then(ResultMapper mapper, ErrorHandler error); + + IPromise Then(ResultMapper mapper); - IPromise Chain(ChainedOperation chained, ErrorHandler error); - IPromise Chain(ChainedOperation chained); + IPromise Then(ChainedOperation chained, ErrorHandler error); + + IPromise Then(ChainedOperation chained); new IPromise Cancelled(Action handler); + new IPromise Finally(Action handler); - new IPromise Anyway(Action handler); - } } diff --git a/Implab/Implab.csproj b/Implab/Implab.csproj --- a/Implab/Implab.csproj +++ b/Implab/Implab.csproj @@ -29,6 +29,25 @@ 4 false + + true + full + false + bin\Debug + TRACE;DEBUG;NET_4_5 + prompt + 4 + true + false + + + true + bin\Release + prompt + 4 + false + NET_4_5 + diff --git a/Implab/Parallels/ArrayTraits.cs b/Implab/Parallels/ArrayTraits.cs --- a/Implab/Parallels/ArrayTraits.cs +++ b/Implab/Parallels/ArrayTraits.cs @@ -29,8 +29,7 @@ namespace Implab.Parallels { m_pending = source.Length; m_action = action; - m_promise.Anyway(() => Dispose()); - m_promise.Cancelled(() => Dispose()); + m_promise.Finally(Dispose); InitPool(); } @@ -48,7 +47,7 @@ namespace Implab.Parallels { protected override bool TryDequeue(out int unit) { unit = Interlocked.Increment(ref m_next) - 1; - return unit >= m_source.Length ? false : true; + return unit < m_source.Length; } protected override void InvokeUnit(int unit) { @@ -86,8 +85,7 @@ namespace Implab.Parallels { m_transform = transform; m_traceContext = TraceContext.Snapshot(); - m_promise.Anyway(() => Dispose()); - m_promise.Cancelled(() => Dispose()); + m_promise.Finally(Dispose); InitPool(); } @@ -157,16 +155,17 @@ namespace Implab.Parallels { var semaphore = new Semaphore(threads, threads); + // Analysis disable AccessToDisposedClosure AsyncPool.InvokeNewThread(() => { for (int i = 0; i < source.Length; i++) { if(promise.IsResolved) break; // stop processing in case of error or cancellation var idx = i; + semaphore.WaitOne(); try { var p1 = transform(source[i]); - p1.Anyway(() => semaphore.Release()); - p1.Cancelled(() => semaphore.Release()); + p1.Finally(() => semaphore.Release()); p1.Then( x => { res[idx] = x; @@ -187,7 +186,7 @@ namespace Implab.Parallels { return 0; }); - return promise.Anyway(() => semaphore.Dispose()); + return promise.Finally(semaphore.Dispose); } } diff --git a/Implab/Promise.cs b/Implab/Promise.cs --- a/Implab/Promise.cs +++ b/Implab/Promise.cs @@ -225,6 +225,18 @@ namespace Implab { Cancel(); } + + public IPromise Then(ResultHandler success, ErrorHandler error, Action cancel) { + if (success == null && error == null && cancel == null) + return this; + + var medium = new Promise(this, true); + + AddHandler(success, error, cancel, medium); + + return medium; + } + /// /// Adds new handlers to this promise. /// @@ -243,6 +255,17 @@ namespace Implab { return medium; } + public IPromise Then(Action success, ErrorHandler error, Action cancel) { + return Then( + x => success(), + e => { + error(e); + return default(T); + }, + cancel + ); + } + public IPromise Then(Action success, ErrorHandler error) { return Then( x => success(), @@ -269,6 +292,39 @@ namespace Implab { return medium; } + public void Last(ResultHandler success, ErrorHandler error, Action cancel) { + if (success == null && error == null && cancel == null) + return; + + ErrorHandler errorHandler = null; + if (error != null) + errorHandler = err => { + error(err); + return default(T); + }; + AddHandler(success, errorHandler, cancel, null); + } + + public void Last(ResultHandler success, ErrorHandler error) { + Last(success, error, null); + } + + public void Last(ResultHandler success) { + Last(success, null, null); + } + + public void Last(Action success,ErrorHandler error, Action cancel) { + Last(x => success(), error, cancel); + } + + public void Last(Action success,ErrorHandler error) { + Last(x => success(), error, null); + } + + public void Last(Action success) { + Last(x => success(), null, null); + } + public IPromise Error(ErrorHandler error) { if (error == null) return this; @@ -307,25 +363,6 @@ namespace Implab { return medium; } - public IPromise Anyway(Action handler) { - if (handler == null) - return this; - - var medium = new Promise(this, true); - - AddHandler( - x => handler(), - e => { - handler(); - throw new TransientPromiseException(e); - }, - null, - medium - ); - - return medium; - } - /// /// Позволяет преобразовать результат выполения операции к новому типу. /// @@ -334,7 +371,7 @@ namespace Implab { /// Обработчик ошибки. Данный обработчик получит /// исключение возникшее при выполнении операции. /// Новое обещание, которое будет выполнено при выполнении исходного обещания. - public IPromise Map(ResultMapper mapper, ErrorHandler error) { + public IPromise Then(ResultMapper mapper, ErrorHandler error) { if (mapper == null) throw new ArgumentNullException("mapper"); @@ -370,8 +407,8 @@ namespace Implab { return chained; } - public IPromise Map(ResultMapper mapper) { - return Map(mapper, null); + public IPromise Then(ResultMapper mapper) { + return Then(mapper, null); } /// @@ -384,7 +421,7 @@ namespace Implab { /// Обработчик ошибки. Данный обработчик получит /// исключение возникшее при выполнении текуещй операции. /// Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции. - public IPromise Chain(ChainedOperation chained, ErrorHandler error) { + public IPromise Then(ChainedOperation chained, ErrorHandler error) { // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно // создать посредника, к которому будут подвызяваться следующие обработчики. @@ -442,8 +479,8 @@ namespace Implab { return medium; } - public IPromise Chain(ChainedOperation chained) { - return Chain(chained, null); + public IPromise Then(ChainedOperation chained) { + return Then(chained, null); } public IPromise Cancelled(Action handler) { @@ -478,7 +515,7 @@ namespace Implab { /// /// public IPromise Cast() { - return Map(x => (T2)(object)x, null); + return Then(x => (T2)(object)x, null); } /// @@ -504,8 +541,7 @@ namespace Implab { /// Результат выполнения обещания public T Join(int timeout) { var evt = new ManualResetEvent(false); - Anyway(() => evt.Set()); - Cancelled(() => evt.Set()); + Finally(() => evt.Set()); if (!evt.WaitOne(timeout, true)) throw new TimeoutException(); @@ -704,10 +740,6 @@ namespace Implab { return Error(error); } - IPromise IPromise.Anyway(Action handler) { - return Anyway(handler); - } - IPromise IPromise.Finally(Action handler) { return Finally(handler); } diff --git a/Implab/PromiseExtensions.cs b/Implab/PromiseExtensions.cs --- a/Implab/PromiseExtensions.cs +++ b/Implab/PromiseExtensions.cs @@ -1,8 +1,13 @@ using System.Threading; +using System; +#if NET_4_5 +using System.Threading.Tasks; +#endif namespace Implab { public static class PromiseExtensions { public static IPromise DispatchToCurrentContext(this IPromise that) { + Safe.ArgumentNotNull(that, "that"); var context = SynchronizationContext.Current; if (context == null) return that; @@ -20,6 +25,7 @@ namespace Implab { } public static IPromise DispatchToContext(this IPromise that, SynchronizationContext context) { + Safe.ArgumentNotNull(that, "that"); Safe.ArgumentNotNull(context, "context"); var p = new SyncContextPromise(context, that, true); @@ -33,6 +39,31 @@ namespace Implab { ); return p; } + + public static AsyncCallback AsyncCallback(this Promise that, Func callback) { + Safe.ArgumentNotNull(that, "that"); + Safe.ArgumentNotNull(callback, "callback"); + return ar => { + try { + that.Resolve(callback(ar)); + } catch (Exception err) { + that.Reject(err); + } + }; + } + + #if NET_4_5 + + public static Task GetTask(this IPromise that) { + Safe.ArgumentNotNull(that, "that"); + var tcs = new TaskCompletionSource(); + + that.Last(tcs.SetResult, tcs.SetException, tcs.SetCanceled); + + return tcs.Task; + } + + #endif } }