Mercurial > pub > ImplabNet
changeset 145:706fccb85524 v2
RC: cancellation support for promises + tests
author | cin |
---|---|
date | Sun, 08 Mar 2015 02:52:27 +0300 |
parents | 8c0b95069066 |
children | e03ccec4a08d |
files | Implab.Fx/ControlBoundPromise.cs Implab.Test/AsyncTests.cs Implab.Test/CancelationTests.cs Implab.Test/Implab.Test.mono.csproj Implab/AbstractEvent.cs Implab/AbstractPromise.cs Implab/AbstractPromiseT.cs Implab/ActionChainTask.cs Implab/ActionChainTaskBase.cs Implab/ActionChainTaskT.cs Implab/ActionTask.cs Implab/ActionTaskBase.cs Implab/ActionTaskT.cs Implab/ChainTask.cs Implab/FuncChainTask.cs Implab/FuncChainTaskBase.cs Implab/FuncChainTaskT.cs Implab/ICancelationToken.cs Implab/ICancellationToken.cs Implab/IDeferred.cs Implab/IDeferredT.cs Implab/ITaskController.cs Implab/Implab.csproj Implab/Parallels/ArrayTraits.cs Implab/Parallels/AsyncPool.cs Implab/PromiseExtensions.cs Implab/Safe.cs Implab/SuccessPromise.cs Implab/SuccessPromiseT.cs Implab/SyncContextPromise.cs MonoPlay/Program.cs |
diffstat | 31 files changed, 1046 insertions(+), 203 deletions(-) [+] |
line wrap: on
line diff
--- a/Implab.Fx/ControlBoundPromise.cs Fri Mar 06 15:45:26 2015 +0300 +++ b/Implab.Fx/ControlBoundPromise.cs Sun Mar 08 02:52:27 2015 +0300 @@ -12,23 +12,23 @@ m_target = target; } - protected override void SignalSuccess(IDeferred<T> handler) { + protected override void SignalSuccess(Promise<T>.HandlerDescriptor handler) { if (m_target.InvokeRequired) - m_target.BeginInvoke(new Action<IDeferred<T>>(base.SignalSuccess), handler); + m_target.BeginInvoke(new Action<Promise<T>.HandlerDescriptor>(base.SignalSuccess), handler); else base.SignalSuccess(handler); } - protected override void SignalCancelled(IDeferred<T> handler, Exception reason) { + protected override void SignalCancelled(Promise<T>.HandlerDescriptor handler, Exception reason) { if (m_target.InvokeRequired) - m_target.BeginInvoke(new Action<IDeferred<T>,Exception>(base.SignalCancelled), handler, reason); + m_target.BeginInvoke(new Action<Promise<T>.HandlerDescriptor,Exception>(base.SignalCancelled), handler, reason); else base.SignalCancelled(handler, reason); } - protected override void SignalError(IDeferred<T> handler, Exception error) { + protected override void SignalError(Promise<T>.HandlerDescriptor handler, Exception error) { if (m_target.InvokeRequired) - m_target.BeginInvoke(new Action<IDeferred<T>,Exception>(base.SignalError), handler, error); + m_target.BeginInvoke(new Action<Promise<T>.HandlerDescriptor,Exception>(base.SignalError), handler, error); else base.SignalError(handler, error); }
--- a/Implab.Test/AsyncTests.cs Fri Mar 06 15:45:26 2015 +0300 +++ b/Implab.Test/AsyncTests.cs Sun Mar 08 02:52:27 2015 +0300 @@ -7,7 +7,7 @@ using NUnit.Framework; using TestClassAttribute = NUnit.Framework.TestFixtureAttribute; -using TestMethod = NUnit.Framework.TestAttribute; +using TestMethodAttribute = NUnit.Framework.TestAttribute; #else @@ -51,7 +51,7 @@ [TestMethod] public void CancelExceptionTest() { var p = new Promise<bool>(); - p.Cancel(); + p.CancelOperation(null); var p2 = p.Then(x => x, null, reason => { throw new ApplicationException("CANCELLED"); @@ -69,10 +69,10 @@ [TestMethod] public void ContinueOnCancelTest() { var p = new Promise<bool>(); - p.Cancel(); + p.CancelOperation(null); var p2 = p - .Then<bool>(x => x, null, reason => { + .Then(x => x, null, reason => { throw new ApplicationException("CANCELLED"); }) .Then(x => x, e => true);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.Test/CancelationTests.cs Sun Mar 08 02:52:27 2015 +0300 @@ -0,0 +1,144 @@ +using System; +using Implab.Parallels; + +#if MONO + +using NUnit.Framework; +using TestClassAttribute = NUnit.Framework.TestFixtureAttribute; +using TestMethodAttribute = NUnit.Framework.TestAttribute; + +#else + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +#endif + +namespace Implab.Test { + [TestClass] + public class CancelationTests { + + [TestMethod] + public void PromiseCancelTest() { + var p = new Promise(); + bool requested = false; + var reason = new Exception("Test"); + + // request cancelation + p.Cancel(reason); + + Assert.IsTrue(p.IsCancellationRequested); + Assert.AreSame(reason, p.CancellationReason); + Assert.IsFalse(p.IsCancelled); + + p.CancellationRequested(r => { + Assert.AreSame(reason, r); + requested = true; + }); + + Assert.IsTrue(requested); + + // cancel the promise + Assert.IsTrue(p.CancelOperationIfRequested()); + Assert.IsTrue(p.IsCancelled); + Assert.AreSame(reason, p.Error); + } + + [TestMethod] + public void CancelActionBeforeStartTask() { + bool run = false; + var task = new ActionTask(() => { + run = true; + }, null, null); + + // request cancelation + task.Cancel(); + Assert.IsTrue(task.IsCancelled); + task.Resolve(); + Assert.IsFalse(run); + } + + [TestMethod] + public void CancelActionAfterTaskStarted() { + var finish = new Signal(); + var started = new Signal(); + + var task = new ActionTask(() => { + started.Set(); + finish.Wait(); + }, null, null); + + AsyncPool.RunThread(() => { + task.Resolve(); + }); + + started.Wait(1000); + + task.Cancel(); + Assert.IsTrue(task.IsCancellationRequested); + Assert.IsFalse(task.IsCancelled); + Assert.IsFalse(task.IsResolved); + + finish.Set(); + task.Join(1000); + + } + + [TestMethod] + public void CancelTaskChainFromBottom() { + var check1 = new Signal(); + var requested = false; + var p1 = AsyncPool.RunThread(token => { + token.CancellationRequested(reason => requested = true); + check1.Wait(); + token.CancelOperationIfRequested(); + }); + + var p2 = p1.Then(() => { + }); + + Assert.IsFalse(p1.IsResolved); + Assert.IsFalse(p2.IsResolved); + + p2.Cancel(); + + Assert.IsFalse(p2.IsCancelled); + Assert.IsFalse(p1.IsCancelled); + Assert.IsTrue(requested); + + check1.Set(); + + try { + p2.Join(1000); + Assert.Fail("The chain isn't cancelled"); + } catch(OperationCanceledException){ + } + + Assert.IsTrue(p1.IsCancelled); + Assert.IsTrue(p2.IsCancelled); + } + + + + [TestMethod] + public void CancellableAsyncTask() { + var finish = new Signal(); + var started = new Signal(); + + var p = AsyncPool.RunThread(token => { + token.CancellationRequested(r => finish.Set()); + started.Set(); + finish.Wait(); + Assert.IsTrue(token.CancelOperationIfRequested()); + }); + + started.Wait(1000); + Assert.IsFalse(p.IsResolved); + p.Cancel(); + try { + p.Join(1000); + } catch (OperationCanceledException) { + } + } + } +} +
--- a/Implab.Test/Implab.Test.mono.csproj Fri Mar 06 15:45:26 2015 +0300 +++ b/Implab.Test/Implab.Test.mono.csproj Sun Mar 08 02:52:27 2015 +0300 @@ -56,6 +56,7 @@ <Compile Include="AsyncTests.cs" /> <Compile Include="PromiseHelper.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="CancelationTests.cs" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\Implab\Implab.csproj">
--- a/Implab/AbstractEvent.cs Fri Mar 06 15:45:26 2015 +0300 +++ b/Implab/AbstractEvent.cs Sun Mar 08 02:52:27 2015 +0300 @@ -4,7 +4,7 @@ using System.Reflection; namespace Implab { - public abstract class AbstractEvent<THandler> : ICancelationToken, ICancellable { + public abstract class AbstractEvent<THandler> : ICancellationToken, ICancellable { const int UNRESOLVED_SATE = 0; const int TRANSITIONAL_STATE = 1; @@ -280,31 +280,34 @@ } } - public bool AcceptIfRequested() { - if (IsCancelRequested) - CancelOperation(CancelReason); + public bool CancelOperationIfRequested() { + if (IsCancellationRequested) { + CancelOperation(CancellationReason); + return true; + } + return false; } public virtual void CancelOperation(Exception reason) { SetCancelled(reason); } - public void CancelationRequested(Action<Exception> handler) { + public void CancellationRequested(Action<Exception> handler) { Safe.ArgumentNotNull(handler, "handler"); - if (IsCancelRequested) - handler(CancelReason); + if (IsCancellationRequested) + handler(CancellationReason); if (m_cancelationHandlers == null) Interlocked.CompareExchange(ref m_cancelationHandlers, new MTQueue<Action<Exception>>(), null); m_cancelationHandlers.Enqueue(handler); - if (IsCancelRequested && m_cancelationHandlers.TryDequeue(out handler)) + if (IsCancellationRequested && m_cancelationHandlers.TryDequeue(out handler)) // TryDeque implies MemoryBarrier() handler(m_cancelationReason); } - public bool IsCancelRequested { + public bool IsCancellationRequested { get { do { if (m_cancelRequest == CANCEL_NOT_REQUESTED) @@ -316,7 +319,7 @@ } } - public Exception CancelReason { + public Exception CancellationReason { get { do { Thread.MemoryBarrier(); @@ -333,7 +336,7 @@ } public void Cancel(Exception reason) { - if (CANCEL_NOT_REQUESTED == Interlocked.CompareExchange(ref m_cancelRequest, CANCEL_REQUESTING)) { + if (CANCEL_NOT_REQUESTED == Interlocked.CompareExchange(ref m_cancelRequest, CANCEL_REQUESTING, CANCEL_NOT_REQUESTED)) { m_cancelationReason = reason; m_cancelRequest = CANCEL_REQUESTED; if (m_cancelationHandlers != null) {
--- a/Implab/AbstractPromise.cs Fri Mar 06 15:45:26 2015 +0300 +++ b/Implab/AbstractPromise.cs Sun Mar 08 02:52:27 2015 +0300 @@ -18,11 +18,13 @@ public HandlerDescriptor(Action handler, PromiseEventType mask) { m_handler = handler; + m_error = null; + m_cancel = null; m_mask = mask; } public void SignalSuccess() { - if (m_mask & PromiseEventType.Success && m_handler != null) { + if ((m_mask & PromiseEventType.Success) != 0 && m_handler != null) { try { m_handler(); } catch (Exception err) { @@ -40,7 +42,7 @@ // Analysis disable once EmptyGeneralCatchClause } catch { } - } else if (m_mask & PromiseEventType.Error && m_handler != null) { + } else if ((m_mask & PromiseEventType.Error ) != 0 && m_handler != null) { try { m_handler(); // Analysis disable once EmptyGeneralCatchClause @@ -56,7 +58,7 @@ } catch (Exception err) { SignalError(err); } - } else if (m_mask & PromiseEventType.Cancelled && m_handler != null) { + } else if ( (m_mask & PromiseEventType.Cancelled) != 0 && m_handler != null) { try { m_handler(); // Analysis disable once EmptyGeneralCatchClause @@ -84,11 +86,11 @@ protected override Signal GetResolveSignal() { var signal = new Signal(); On(signal.Set, PromiseEventType.All); + return signal; } #endregion - public Type PromiseType { get { return typeof(void);
--- a/Implab/AbstractPromiseT.cs Fri Mar 06 15:45:26 2015 +0300 +++ b/Implab/AbstractPromiseT.cs Sun Mar 08 02:52:27 2015 +0300 @@ -14,10 +14,14 @@ m_success = success; m_error = error; m_cancel = cancel; + + m_handler = null; + m_mask = 0; } public HandlerDescriptor(Action success, Action<Exception> error, Action<Exception> cancel) { m_handler = success; + m_success = null; m_error = error; m_cancel = cancel; m_mask = PromiseEventType.Success; @@ -26,6 +30,9 @@ public HandlerDescriptor(Action handler, PromiseEventType mask) { m_handler = handler; m_mask = mask; + m_success = null; + m_error = null; + m_cancel = null; } public void SignalSuccess(T result) { @@ -35,7 +42,7 @@ } catch(Exception err) { SignalError(err); } - } else if (m_mask & PromiseEventType.Success && m_handler != null) { + } else if ((m_mask & PromiseEventType.Success) != 0 && m_handler != null) { try { m_handler(); } catch(Exception err) { @@ -53,7 +60,7 @@ // Analysis disable once EmptyGeneralCatchClause } catch { } - } else if (m_mask & PromiseEventType.Error && m_handler != null) { + } else if ((m_mask & PromiseEventType.Error) != 0 && m_handler != null) { try { m_handler(); // Analysis disable once EmptyGeneralCatchClause @@ -69,7 +76,7 @@ } catch (Exception err) { SignalError(err); } - } else if (m_mask & PromiseEventType.Cancelled && m_handler != null) { + } else if ((m_mask & PromiseEventType.Cancelled) != 0 && m_handler != null) { try { m_handler(); // Analysis disable once EmptyGeneralCatchClause @@ -79,23 +86,28 @@ } } - - public Type PromiseType { get { return typeof(T); } } - public new T Join() { + public T Join() { WaitResult(-1); return m_result; } - public new T Join(int timeout) { + public T Join(int timeout) { WaitResult(timeout); return m_result; } + void IPromise.Join() { + WaitResult(-1); + } + void IPromise.Join(int timeout) { + WaitResult(timeout); + } + public IPromise<T> On(Action<T> success, Action<Exception> error, Action<Exception> cancel) { AddHandler(new HandlerDescriptor(success, error, cancel)); return this; @@ -146,6 +158,11 @@ return this; } + IPromise IPromise.On(Action handler, PromiseEventType events) { + AddHandler(new HandlerDescriptor(handler, events)); + return this; + } + public IPromise<T2> Cast<T2>() { return (IPromise<T2>)this; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/ActionChainTask.cs Sun Mar 08 02:52:27 2015 +0300 @@ -0,0 +1,23 @@ +using System; + +namespace Implab { + public class ActionChainTask : ActionChainTaskBase, IDeferred { + readonly Func<IPromise> m_task; + + public ActionChainTask(Func<IPromise> task, Func<Exception, IPromise> error, Func<Exception, IPromise> cancel) : base(error,cancel) { + m_task = task; + } + + public void Resolve() { + if (m_task != null && LockCancelation()) { + try { + Observe(m_task()); + } catch(Exception err) { + HandleErrorInternal(err); + } + } + } + + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/ActionChainTaskBase.cs Sun Mar 08 02:52:27 2015 +0300 @@ -0,0 +1,62 @@ +using System; +using System.Threading; + +namespace Implab { + public class ActionChainTaskBase : AbstractPromise { + readonly Func<Exception, IPromise> m_error; + readonly Func<Exception, IPromise> m_cancel; + + int m_cancelationLock; + + protected ActionChainTaskBase( Func<Exception, IPromise> error, Func<Exception, IPromise> cancel) { + m_error = error; + m_cancel = cancel; + } + + public void Reject(Exception error) { + if (LockCancelation()) + HandleErrorInternal(error); + } + + + + public override void CancelOperation(Exception reason) { + if (m_cancel != null && LockCancelation()) { + try { + Observe(m_cancel(reason)); + } catch(Exception err) { + HandleErrorInternal(err); + } + } + + } + + protected void HandleErrorInternal(Exception error) { + if (m_error != null) { + try { + Observe(m_error(error)); + } catch(Exception err) { + SetError(err); + } + } else { + SetError(error); + } + } + + protected void Observe(IPromise operation) { + if (operation == null) + throw new NullReferenceException("The task returned null promise"); + + // pass operation results to the current promise + operation.On(SetResult, SetError, SetCancelled); + + // pass the cancelation request + CancellationRequested(operation.Cancel); + } + + protected bool LockCancelation() { + return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0); + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/ActionChainTaskT.cs Sun Mar 08 02:52:27 2015 +0300 @@ -0,0 +1,23 @@ +using System; + +namespace Implab { + public class ActionChainTask<T> : ActionChainTaskBase, IDeferred<T> { + readonly Func<T, IPromise> m_task; + + public ActionChainTask(Func<T, IPromise> task, Func<Exception, IPromise> error, Func<Exception, IPromise> cancel) : base(error,cancel) { + m_task = task; + } + + public void Resolve(T value) { + if (m_task != null && LockCancelation()) { + try { + Observe(m_task(value)); + } catch(Exception err) { + HandleErrorInternal(err); + } + } + } + + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/ActionTask.cs Sun Mar 08 02:52:27 2015 +0300 @@ -0,0 +1,22 @@ +using System; + +namespace Implab { + public class ActionTask : ActionTaskBase, IDeferred { + readonly Action m_task; + public ActionTask(Action task, Action<Exception> error, Action<Exception> cancel) : base(error,cancel) { + m_task = task; + } + + public void Resolve() { + if (m_task != null && LockCancelation()) { + try { + m_task(); + SetResult(); + } catch(Exception err) { + HandleErrorInternal(err); + } + } + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/ActionTaskBase.cs Sun Mar 08 02:52:27 2015 +0300 @@ -0,0 +1,55 @@ +using System; +using System.Threading; + +namespace Implab { + public class ActionTaskBase : AbstractPromise { + readonly Action<Exception> m_cancel; + readonly Action<Exception> m_error; + + int m_cancelationLock; + + protected ActionTaskBase( Action<Exception> error, Action<Exception> cancel) { + m_error = error; + m_cancel = cancel; + } + + public void Reject(Exception error) { + Safe.ArgumentNotNull(error, "error"); + if (LockCancelation()) + HandleErrorInternal(error); + } + + protected void HandleErrorInternal(Exception error) { + if (m_error != null) { + try { + m_error(error); + SetResult(); + } catch(Exception err) { + SetError(err); + } + } else { + SetError(error); + } + } + + public override void CancelOperation(Exception reason) { + if (LockCancelation()) { + if (m_cancel != null) { + try { + m_cancel(reason); + SetResult(); + } catch (Exception err) { + HandleErrorInternal(err); + } + } else { + SetCancelled(reason); + } + } + } + + protected bool LockCancelation() { + return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0); + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/ActionTaskT.cs Sun Mar 08 02:52:27 2015 +0300 @@ -0,0 +1,22 @@ +using System; + +namespace Implab { + public class ActionTask<T> : ActionTaskBase, IDeferred<T> { + readonly Action<T> m_task; + public ActionTask(Action<T> task, Action<Exception> error, Action<Exception> cancel) : base(error,cancel) { + m_task = task; + } + + public void Resolve(T value) { + if (m_task != null && LockCancelation()) { + try { + m_task(value); + SetResult(); + } catch(Exception err) { + HandleErrorInternal(err); + } + } + } + } +} +
--- a/Implab/ChainTask.cs Fri Mar 06 15:45:26 2015 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ -using System; -using System.Threading; - -namespace Implab { - public class ChainTask : AbstractPromise, IDeferred { - readonly Func<IPromise> m_task; - readonly Action<Exception> m_error; - readonly Action<Exception> m_cancel; - - int m_cancelationLock; - - public ChainTask(Func<IPromise> task, Func<Exception> error, Func<Exception> cancel) { - m_task = task; - } - - public void Resolve() { - if (m_task != null && LockCancelation()) { - try { - var operation = m_task(); - if (operation == null) - throw new NullReferenceException("The task returned null promise"); - - operation.On(SetResult, SetError, SetCancelled); - - CancelationRequested(operation.Cancel); - } catch(Exception err) { - HandleErrorInternal(err); - } - } - } - - public void Reject(Exception error) { - throw new NotImplementedException(); - } - - protected void HandleErrorInternal(Exception error) { - if (m_error != null) { - try { - m_error(error); - SetResult(); - } catch(Exception err) { - SetError(err); - } - } else { - SetError(error); - } - } - - protected bool LockCancelation() { - return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0); - } - } -} -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/FuncChainTask.cs Sun Mar 08 02:52:27 2015 +0300 @@ -0,0 +1,21 @@ +using System; + +namespace Implab { + public class FuncChainTask<TResult> : FuncChainTaskBase<TResult>, IDeferred { + readonly Func<IPromise<TResult>> m_task; + + public FuncChainTask(Func<IPromise<TResult>> task, Func<Exception, IPromise<TResult>> error, Func<Exception, IPromise<TResult>> cancel) : base(error, cancel){ + m_task = task; + } + + public void Resolve() { + if (m_task != null && LockCancelation()) { + try { + Observe(m_task()); + } catch (Exception err) { + HandleErrorInternal(err); + } + } + } + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/FuncChainTaskBase.cs Sun Mar 08 02:52:27 2015 +0300 @@ -0,0 +1,60 @@ +using System; +using System.Threading; + +namespace Implab { + public class FuncChainTaskBase<TResult> : AbstractPromise<TResult> { + readonly Func<Exception, IPromise<TResult>> m_error; + readonly Func<Exception, IPromise<TResult>> m_cancel; + + int m_cancelationLock; + + protected FuncChainTaskBase( Func<Exception, IPromise<TResult>> error, Func<Exception, IPromise<TResult>> cancel) { + m_error = error; + m_cancel = cancel; + } + + public void Reject(Exception error) { + if (LockCancelation()) + HandleErrorInternal(error); + } + + public override void CancelOperation(Exception reason) { + if (m_cancel != null && LockCancelation()) { + try { + Observe(m_cancel(reason)); + } catch(Exception err) { + HandleErrorInternal(err); + } + } + + } + + protected void HandleErrorInternal(Exception error) { + if (m_error != null) { + try { + Observe(m_error(error)); + } catch(Exception err) { + SetError(err); + } + } else { + SetError(error); + } + } + + protected void Observe(IPromise<TResult> operation) { + if (operation == null) + throw new NullReferenceException("The task returned null promise"); + + // pass operation results to the current promise + operation.On(SetResult, SetError, SetCancelled); + + // pass the cancelation request + CancellationRequested(operation.Cancel); + } + + protected bool LockCancelation() { + return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0); + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/FuncChainTaskT.cs Sun Mar 08 02:52:27 2015 +0300 @@ -0,0 +1,21 @@ +using System; + +namespace Implab { + public class FuncChainTask<TArg,TResult> : FuncChainTaskBase<TResult>, IDeferred<TArg> { + readonly Func<TArg, IPromise<TResult>> m_task; + + public FuncChainTask(Func<TArg, IPromise<TResult>> task, Func<Exception, IPromise<TResult>> error, Func<Exception, IPromise<TResult>> cancel) : base(error, cancel){ + m_task = task; + } + + public void Resolve(TArg value) { + if (m_task != null && LockCancelation()) { + try { + Observe(m_task(value)); + } catch (Exception err) { + HandleErrorInternal(err); + } + } + } + } +} \ No newline at end of file
--- a/Implab/ICancelationToken.cs Fri Mar 06 15:45:26 2015 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -using System; - -namespace Implab { - public interface ICancelationToken { - /// <summary> - /// Indicates wherther the cancellation was requested. - /// </summary> - bool IsCancelRequested { get ; } - - /// <summary> - /// The reason why the operation should be cancelled. - /// </summary> - Exception CancelReason { get ; } - - /// <summary> - /// Accepts if requested. - /// </summary> - /// <returns><c>true</c>, if if requested was accepted, <c>false</c> otherwise.</returns> - bool AcceptIfRequested(); - - /// <summary> - /// Sets the token to cancelled state. - /// </summary> - /// <param name="reason">The reason why the operation was cancelled.</param> - void CancelOperation(Exception reason); - - /// <summary> - /// Adds the listener for the cancellation request, is the cancellation was requested the <paramref name="handler"/> - /// is executed immediatelly. - /// </summary> - /// <param name="handler">The handler which will be executed if the cancel occurs.</param> - void CancelationRequested(Action<Exception> handler); - - } -} -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/ICancellationToken.cs Sun Mar 08 02:52:27 2015 +0300 @@ -0,0 +1,36 @@ +using System; + +namespace Implab { + public interface ICancellationToken { + /// <summary> + /// Indicates wherther the cancellation was requested. + /// </summary> + bool IsCancellationRequested { get ; } + + /// <summary> + /// The reason why the operation should be cancelled. + /// </summary> + Exception CancellationReason { get ; } + + /// <summary> + /// Accepts if requested. + /// </summary> + /// <returns><c>true</c>, if if requested was accepted, <c>false</c> otherwise.</returns> + bool CancelOperationIfRequested(); + + /// <summary> + /// Sets the token to cancelled state. + /// </summary> + /// <param name="reason">The reason why the operation was cancelled.</param> + void CancelOperation(Exception reason); + + /// <summary> + /// Adds the listener for the cancellation request, is the cancellation was requested the <paramref name="handler"/> + /// is executed immediatelly. + /// </summary> + /// <param name="handler">The handler which will be executed if the cancel occurs.</param> + void CancellationRequested(Action<Exception> handler); + + } +} +
--- a/Implab/IDeferred.cs Fri Mar 06 15:45:26 2015 +0300 +++ b/Implab/IDeferred.cs Sun Mar 08 02:52:27 2015 +0300 @@ -4,7 +4,7 @@ /// <summary> /// Deferred result, usually used by asynchronous services as the service part of the promise. /// </summary> - public interface IDeferred : ICancelationToken { + public interface IDeferred : ICancellationToken { void Resolve();
--- a/Implab/IDeferredT.cs Fri Mar 06 15:45:26 2015 +0300 +++ b/Implab/IDeferredT.cs Sun Mar 08 02:52:27 2015 +0300 @@ -1,7 +1,7 @@ using System; namespace Implab { - public interface IDeferred<T> : ICancelationToken { + public interface IDeferred<in T> : ICancellationToken { void Resolve(T value); void Reject(Exception error);
--- a/Implab/ITaskController.cs Fri Mar 06 15:45:26 2015 +0300 +++ b/Implab/ITaskController.cs Sun Mar 08 02:52:27 2015 +0300 @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace Implab { public interface ITaskController: IProgressHandler, ICancellable {
--- a/Implab/Implab.csproj Fri Mar 06 15:45:26 2015 +0300 +++ b/Implab/Implab.csproj Sun Mar 08 02:52:27 2015 +0300 @@ -157,14 +157,24 @@ <Compile Include="Diagnostics\ILogWriter.cs" /> <Compile Include="Diagnostics\ListenerBase.cs" /> <Compile Include="Parallels\BlockingQueue.cs" /> - <Compile Include="ICancelationToken.cs" /> <Compile Include="AbstractEvent.cs" /> <Compile Include="AbstractPromise.cs" /> <Compile Include="AbstractPromiseT.cs" /> <Compile Include="FuncTask.cs" /> <Compile Include="FuncTaskBase.cs" /> <Compile Include="FuncTaskT.cs" /> - <Compile Include="ChainTask.cs" /> + <Compile Include="ActionChainTaskBase.cs" /> + <Compile Include="ActionChainTask.cs" /> + <Compile Include="ActionChainTaskT.cs" /> + <Compile Include="FuncChainTaskBase.cs" /> + <Compile Include="FuncChainTask.cs" /> + <Compile Include="FuncChainTaskT.cs" /> + <Compile Include="ActionTaskBase.cs" /> + <Compile Include="ActionTask.cs" /> + <Compile Include="ActionTaskT.cs" /> + <Compile Include="ICancellationToken.cs" /> + <Compile Include="SuccessPromise.cs" /> + <Compile Include="SuccessPromiseT.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <ItemGroup />
--- a/Implab/Parallels/ArrayTraits.cs Fri Mar 06 15:45:26 2015 +0300 +++ b/Implab/Parallels/ArrayTraits.cs Sun Mar 08 02:52:27 2015 +0300 @@ -152,7 +152,7 @@ throw new ArgumentOutOfRangeException("threads","Threads number must be greater then zero"); if (source.Length == 0) - return Promise<TDst[]>.ResultToPromise(new TDst[0]); + return Promise<TDst[]>.FromResult(new TDst[0]); var promise = new Promise<TDst[]>(); var res = new TDst[source.Length];
--- a/Implab/Parallels/AsyncPool.cs Fri Mar 06 15:45:26 2015 +0300 +++ b/Implab/Parallels/AsyncPool.cs Sun Mar 08 02:52:27 2015 +0300 @@ -31,6 +31,24 @@ return p; } + public static IPromise<T> Invoke<T>(Func<ICancellationToken, T> func) { + var p = new Promise<T>(); + var caller = TraceContext.Instance.CurrentOperation; + + ThreadPool.QueueUserWorkItem(param => { + TraceContext.Instance.EnterLogicalOperation(caller,false); + try { + p.Resolve(func(p)); + } catch(Exception e) { + p.Reject(e); + } finally { + TraceContext.Instance.Leave(); + } + }); + + return p; + } + public static IPromise<T> RunThread<T>(Func<T> func) { var p = new Promise<T>(); @@ -52,6 +70,27 @@ return p; } + public static IPromise<T> RunThread<T>(Func<ICancellationToken, T> func) { + var p = new Promise<T>(); + + var caller = TraceContext.Instance.CurrentOperation; + + var worker = new Thread(() => { + TraceContext.Instance.EnterLogicalOperation(caller,false); + try { + p.Resolve(func(p)); + } catch (Exception e) { + p.Reject(e); + } finally { + TraceContext.Instance.Leave(); + } + }); + worker.IsBackground = true; + worker.Start(); + + return p; + } + public static IPromise RunThread(Action func) { var p = new Promise(); @@ -75,12 +114,42 @@ return p; } + public static IPromise RunThread(Action<ICancellationToken> func) { + var p = new Promise(); + + var caller = TraceContext.Instance.CurrentOperation; + + var worker = new Thread(() => { + TraceContext.Instance.EnterLogicalOperation(caller,false); + try { + func(p); + p.Resolve(); + } catch (Exception e) { + p.Reject(e); + } finally { + TraceContext.Instance.Leave(); + } + }); + worker.IsBackground = true; + worker.Start(); + + return p; + } + public static IPromise[] RunThread(params Action[] func) { return func.Select(f => RunThread(f)).ToArray(); } + public static IPromise[] RunThread(params Action<ICancellationToken>[] func) { + return func.Select(f => RunThread(f)).ToArray(); + } + public static IPromise<T>[] RunThread<T>(params Func<T>[] func) { return func.Select(f => RunThread(f)).ToArray(); } + + public static IPromise<T>[] RunThread<T>(params Func<ICancellationToken, T>[] func) { + return func.Select(f => RunThread(f)).ToArray(); + } } }
--- a/Implab/PromiseExtensions.cs Fri Mar 06 15:45:26 2015 +0300 +++ b/Implab/PromiseExtensions.cs Sun Mar 08 02:52:27 2015 +0300 @@ -174,6 +174,116 @@ return medium; } + + public static IPromise Then(this IPromise that, Action success, Action<Exception> error, Action<Exception> cancel) { + Safe.ArgumentNotNull(that, "that"); + + var d = new ActionTask(success, error, cancel); + that.On(d.Resolve, d.Reject, d.CancelOperation); + if (success != null) + d.CancellationRequested(that.Cancel); + return d; + } + + public static IPromise Then(this IPromise that, Action success, Action<Exception> error) { + return Then(that, success, error, null); + } + + public static IPromise Then(this IPromise that, Action success) { + return Then(that, success, null, null); + } + + public static IPromise<T> Then<T>(this IPromise that, Func<T> success, Func<Exception, T> error, Func<Exception, T> cancel) { + Safe.ArgumentNotNull(that, "that"); + + var d = new FuncTask<T>(success, error, cancel); + that.On(d.Resolve, d.Reject, d.CancelOperation); + if (success != null) + d.CancellationRequested(that.Cancel); + return d; + } + + public static IPromise<T> Then<T>(this IPromise that, Func<T> success, Func<Exception, T> error) { + return Then(that, success, error, null); + } + + public static IPromise<T> Then<T>(this IPromise that, Func<T> success) { + return Then(that, success, null, null); + } + + public static IPromise<T2> Then<T, T2>(this IPromise<T> that, Func<T, T2> success, Func<Exception, T2> error, Func<Exception, T2> cancel) { + Safe.ArgumentNotNull(that, "that"); + var d = new FuncTask<T,T2>(success, error, cancel); + that.On(d.Resolve, d.Reject, d.CancelOperation); + if (success != null) + d.CancellationRequested(that.Cancel); + return d; + } + + public static IPromise<T2> Then<T, T2>(this IPromise<T> that, Func<T, T2> success, Func<Exception, T2> error) { + return Then(that, success, error, null); + } + + public static IPromise<T2> Then<T, T2>(this IPromise<T> that, Func<T, T2> success) { + return Then(that, success, null, null); + } + + #region chain traits + public static IPromise Chain(this IPromise that, Func<IPromise> success, Func<Exception,IPromise> error, Func<Exception,IPromise> cancel) { + Safe.ArgumentNotNull(that, "that"); + + var d = new ActionChainTask(success, error, cancel); + that.On(d.Resolve, d.Reject, d.CancelOperation); + if (success != null) + d.CancellationRequested(that.Cancel); + return d; + } + + public static IPromise Chain(this IPromise that, Func<IPromise> success, Func<Exception,IPromise> error) { + return Chain(that, success, error, null); + } + + public static IPromise Chain(this IPromise that, Func<IPromise> success) { + return Chain(that, success, null, null); + } + + public static IPromise<T> Chain<T>(this IPromise that, Func<IPromise<T>> success, Func<Exception, IPromise<T>> error, Func<Exception, IPromise<T>> cancel) { + Safe.ArgumentNotNull(that, "that"); + + var d = new FuncChainTask<T>(success, error, cancel); + that.On(d.Resolve, d.Reject, d.CancelOperation); + if (success != null) + d.CancellationRequested(that.Cancel); + return d; + } + + public static IPromise<T> Chain<T>(this IPromise that, Func<IPromise<T>> success, Func<Exception, IPromise<T>> error) { + return Chain(that, success, error, null); + } + + public static IPromise<T> Chain<T>(this IPromise that, Func<IPromise<T>> success) { + return Chain(that, success, null, null); + } + + public static IPromise<T2> Chain<T, T2>(this IPromise<T> that, Func<T, IPromise<T2>> success, Func<Exception, IPromise<T2>> error, Func<Exception, IPromise<T2>> cancel) { + Safe.ArgumentNotNull(that, "that"); + var d = new FuncChainTask<T,T2>(success, error, cancel); + that.On(d.Resolve, d.Reject, d.CancelOperation); + if (success != null) + d.CancellationRequested(that.Cancel); + return d; + } + + public static IPromise<T2> Chain<T, T2>(this IPromise<T> that, Func<T, IPromise<T2>> success, Func<Exception, IPromise<T2>> error) { + return Chain(that, success, error, null); + } + + public static IPromise<T2> Chain<T, T2>(this IPromise<T> that, Func<T, IPromise<T2>> success) { + return Chain(that, success, null, null); + } + + #endregion + #if NET_4_5
--- a/Implab/Safe.cs Fri Mar 06 15:45:26 2015 +0300 +++ b/Implab/Safe.cs Sun Mar 08 02:52:27 2015 +0300 @@ -109,9 +109,9 @@ ArgumentNotNull(action, "action"); try { - return action() ?? Promise<T>.ExceptionToPromise(new Exception("The action returned null")); + return action() ?? Promise<T>.FromException(new Exception("The action returned null")); } catch (Exception err) { - return Promise<T>.ExceptionToPromise(err); + return Promise<T>.FromException(err); } } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/SuccessPromise.cs Sun Mar 08 02:52:27 2015 +0300 @@ -0,0 +1,111 @@ +using System; + +namespace Implab { + public class SuccessPromise : IPromise { + #region IPromise implementation + + public IPromise On(Action success, Action<Exception> error, Action<Exception> cancel) { + if (success != null) { + try { + success(); + } catch(Exception err) { + if (error != null) { + try { + error(err); + // Analysis disable once EmptyGeneralCatchClause + } catch { + } + } + } + } + return this; + } + + public IPromise On(Action success, Action<Exception> error) { + if (success != null) { + try { + success(); + } catch(Exception err) { + if (error != null) { + try { + error(err); + // Analysis disable once EmptyGeneralCatchClause + } catch { + } + } + } + } + return this; + } + + public IPromise On(Action success) { + if (success != null) { + try { + success(); + // Analysis disable once EmptyGeneralCatchClause + } catch { + } + } + return this; + } + + public IPromise On(Action handler, PromiseEventType events) { + if (handler != null && events.HasFlag(PromiseEventType.Success)) { + try { + handler(); + // Analysis disable once EmptyGeneralCatchClause + } catch { + } + } + return this; + } + + public IPromise<T> Cast<T>() { + throw new InvalidCastException(); + } + + public void Join() { + } + + public void Join(int timeout) { + } + + public Type PromiseType { + get { + return typeof(void); + } + } + + public bool IsResolved { + get { + return true; + } + } + + public bool IsCancelled { + get { + return false; + } + } + + public Exception Error { + get { + return null; + } + } + + #endregion + + #region ICancellable implementation + + public void Cancel() { + } + + public void Cancel(Exception reason) { + } + + #endregion + + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/SuccessPromiseT.cs Sun Mar 08 02:52:27 2015 +0300 @@ -0,0 +1,177 @@ +using System; + +namespace Implab { + public class SuccessPromise<T> : IPromise<T> { + readonly T m_value; + + public SuccessPromise(T value){ + m_value = value; + } + + public IPromise<T> On(Action<T> success, Action<Exception> error, Action<Exception> cancel) { + if (success != null) { + try { + success(m_value); + } catch(Exception err) { + if (error != null) { + try { + error(err); + // Analysis disable once EmptyGeneralCatchClause + } catch { + } + } + } + } + return this; + } + + public IPromise<T> On(Action<T> success, Action<Exception> error) { + if (success != null) { + try { + success(m_value); + } catch(Exception err) { + if (error != null) { + try { + error(err); + // Analysis disable once EmptyGeneralCatchClause + } catch { + } + } + } + } + return this; + } + + public IPromise<T> On(Action<T> success) { + if (success != null) { + try { + success(m_value); + // Analysis disable once EmptyGeneralCatchClause + } catch { + } + } + return this; + } + + public T Join() { + return m_value; + } + + public T Join(int timeout) { + return m_value; + } + + public IPromise<T> On(Action success, Action<Exception> error, Action<Exception> cancel) { + if (success != null) { + try { + success(); + } catch(Exception err) { + if (error != null) { + try { + error(err); + // Analysis disable once EmptyGeneralCatchClause + } catch { + } + } + } + } + return this; + } + + public IPromise<T> On(Action success, Action<Exception> error) { + if (success != null) { + try { + success(); + } catch(Exception err) { + if (error != null) { + try { + error(err); + // Analysis disable once EmptyGeneralCatchClause + } catch { + } + } + } + } + return this; + } + + public IPromise<T> On(Action success) { + if (success != null) { + try { + success(); + // Analysis disable once EmptyGeneralCatchClause + } catch { + } + } + return this; + } + + public IPromise<T> On(Action handler, PromiseEventType events) { + if (handler != null && events.HasFlag(PromiseEventType.Success)) { + try { + handler(); + // Analysis disable once EmptyGeneralCatchClause + } catch { + } + } + return this; + } + + IPromise IPromise.On(Action success, Action<Exception> error, Action<Exception> cancel) { + return On(success, error, cancel); + } + + IPromise IPromise.On(Action success, Action<Exception> error) { + return On(success, error); + } + + IPromise IPromise.On(Action success) { + return On(success); + } + + IPromise IPromise.On(Action handler, PromiseEventType events) { + return On(handler, events); + } + + public IPromise<T2> Cast<T2>() { + return new SuccessPromise<T2>((T2)(object)m_value); + } + + void IPromise.Join() { + } + + void IPromise.Join(int timeout) { + } + + public Type PromiseType { + get { + return typeof(T); + } + } + + public bool IsResolved { + get { + return true; + } + } + + public bool IsCancelled { + get { + return false; + } + } + + public Exception Error { + get { + return null; + } + } + + public void Cancel() { + } + + public void Cancel(Exception reason) { + } + } +} +
--- a/Implab/SyncContextPromise.cs Fri Mar 06 15:45:26 2015 +0300 +++ b/Implab/SyncContextPromise.cs Sun Mar 08 02:52:27 2015 +0300 @@ -10,15 +10,15 @@ m_context = context; } - protected override void SignalSuccess(IDeferred<T> handler) { + protected override void SignalSuccess(Promise<T>.HandlerDescriptor handler) { m_context.Post(x => base.SignalSuccess(handler), null); } - protected override void SignalError(IDeferred<T> handler, Exception error) { + protected override void SignalError(Promise<T>.HandlerDescriptor handler, Exception error) { m_context.Post(x => base.SignalError(handler, error), null); } - protected override void SignalCancelled(IDeferred<T> handler, Exception reason) { + protected override void SignalCancelled(Promise<T>.HandlerDescriptor handler, Exception reason) { m_context.Post(x => base.SignalCancelled(handler, reason), null); } }
--- a/MonoPlay/Program.cs Fri Mar 06 15:45:26 2015 +0300 +++ b/MonoPlay/Program.cs Sun Mar 08 02:52:27 2015 +0300 @@ -8,86 +8,33 @@ namespace MonoPlay { class MainClass { + + public static void Main(string[] args) { if (args == null) throw new ArgumentNullException("args"); var t1 = Environment.TickCount; - const int reads = 100000; - const int writes = 1000; - const int readThreads = 8; - const int writeThreads = 0; - - var l = new SharedLock(); - var st = new HashSet<int>(); - - Action reader1 = () => { - for (int i =0; i < reads; i++) { - try { - l.LockShared(); - st.Contains(i % 1000); - Thread.Sleep(0); - } finally { - l.Release(); - } - } - }; - - Action reader2 = () => { - for(var i = 0; i < reads; i++) - lock(st) { - st.Contains(i % 1000); - Thread.Sleep(0); - } - }; - - Action writer1 = () => { - var rnd = new Random(Environment.TickCount); - for (int i = 0; i < writes; i++) { - try { - l.LockExclusive(); - st.Add(rnd.Next(1000)); - //Thread.Sleep(1); - } finally { - l.Release(); - } - } - }; - - Action writer2 = () => { - var rnd = new Random(Environment.TickCount); - for (int i = 0; i < writes; i++) { - lock (st) { - st.Add(rnd.Next(1000)); - //Thread.Sleep(1); - } - } - }; - - - - var readers = new IPromise[readThreads]; - for (int i = 0; i < readThreads; i++) - readers[i] = AsyncPool.RunThread(reader2); - - var writers = new IPromise[writeThreads]; - for (int i = 0; i < writeThreads; i++) - writers[i] = AsyncPool.RunThread(writer1); - - - new [] { - readers.Bundle().On(() => Console.WriteLine("readers complete in {0} ms", Environment.TickCount - t1)), - writers.Bundle().On(() => Console.WriteLine("writers complete in {0} ms", Environment.TickCount - t1)) - }.Bundle().Join(); - - + for (int i = 0; i < 10000000; i++) { + + var p = new Promise<int>(); + p.On(HandleResult); + p.Resolve(i); + } var t2 = Environment.TickCount; Console.WriteLine("done: {0} ms, {1:.00} Mb, {2} GC", t2 - t1, GC.GetTotalMemory(false) / (1024*1024), GC.CollectionCount(0) ); } + static void HandleAction () + { + + } + static void HandleResult(int x) { + + } } }