diff --git a/Implab.Fx/ControlBoundPromise.cs b/Implab.Fx/ControlBoundPromise.cs --- a/Implab.Fx/ControlBoundPromise.cs +++ b/Implab.Fx/ControlBoundPromise.cs @@ -12,23 +12,23 @@ namespace Implab.Fx { m_target = target; } - protected override void SignalSuccess(IDeferred handler) { + protected override void SignalSuccess(Promise.HandlerDescriptor handler) { if (m_target.InvokeRequired) - m_target.BeginInvoke(new Action>(base.SignalSuccess), handler); + m_target.BeginInvoke(new Action.HandlerDescriptor>(base.SignalSuccess), handler); else base.SignalSuccess(handler); } - protected override void SignalCancelled(IDeferred handler, Exception reason) { + protected override void SignalCancelled(Promise.HandlerDescriptor handler, Exception reason) { if (m_target.InvokeRequired) - m_target.BeginInvoke(new Action,Exception>(base.SignalCancelled), handler, reason); + m_target.BeginInvoke(new Action.HandlerDescriptor,Exception>(base.SignalCancelled), handler, reason); else base.SignalCancelled(handler, reason); } - protected override void SignalError(IDeferred handler, Exception error) { + protected override void SignalError(Promise.HandlerDescriptor handler, Exception error) { if (m_target.InvokeRequired) - m_target.BeginInvoke(new Action,Exception>(base.SignalError), handler, error); + m_target.BeginInvoke(new Action.HandlerDescriptor,Exception>(base.SignalError), handler, error); else base.SignalError(handler, error); } diff --git a/Implab.Test/AsyncTests.cs b/Implab.Test/AsyncTests.cs --- a/Implab.Test/AsyncTests.cs +++ b/Implab.Test/AsyncTests.cs @@ -7,7 +7,7 @@ using Implab.Parallels; using NUnit.Framework; using TestClassAttribute = NUnit.Framework.TestFixtureAttribute; -using TestMethod = NUnit.Framework.TestAttribute; +using TestMethodAttribute = NUnit.Framework.TestAttribute; #else @@ -51,7 +51,7 @@ namespace Implab.Test { [TestMethod] public void CancelExceptionTest() { var p = new Promise(); - p.Cancel(); + p.CancelOperation(null); var p2 = p.Then(x => x, null, reason => { throw new ApplicationException("CANCELLED"); @@ -69,10 +69,10 @@ namespace Implab.Test { [TestMethod] public void ContinueOnCancelTest() { var p = new Promise(); - p.Cancel(); + p.CancelOperation(null); var p2 = p - .Then(x => x, null, reason => { + .Then(x => x, null, reason => { throw new ApplicationException("CANCELLED"); }) .Then(x => x, e => true); diff --git a/Implab.Test/CancelationTests.cs b/Implab.Test/CancelationTests.cs new file mode 100644 --- /dev/null +++ b/Implab.Test/CancelationTests.cs @@ -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) { + } + } + } +} + diff --git a/Implab.Test/Implab.Test.mono.csproj b/Implab.Test/Implab.Test.mono.csproj --- a/Implab.Test/Implab.Test.mono.csproj +++ b/Implab.Test/Implab.Test.mono.csproj @@ -56,6 +56,7 @@ + diff --git a/Implab/AbstractEvent.cs b/Implab/AbstractEvent.cs --- a/Implab/AbstractEvent.cs +++ b/Implab/AbstractEvent.cs @@ -4,7 +4,7 @@ using System.Threading; using System.Reflection; namespace Implab { - public abstract class AbstractEvent : ICancelationToken, ICancellable { + public abstract class AbstractEvent : ICancellationToken, ICancellable { const int UNRESOLVED_SATE = 0; const int TRANSITIONAL_STATE = 1; @@ -280,31 +280,34 @@ namespace Implab { } } - 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 handler) { + public void CancellationRequested(Action handler) { Safe.ArgumentNotNull(handler, "handler"); - if (IsCancelRequested) - handler(CancelReason); + if (IsCancellationRequested) + handler(CancellationReason); if (m_cancelationHandlers == null) Interlocked.CompareExchange(ref m_cancelationHandlers, new MTQueue>(), 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 @@ namespace Implab { } } - public Exception CancelReason { + public Exception CancellationReason { get { do { Thread.MemoryBarrier(); @@ -333,7 +336,7 @@ namespace Implab { } 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) { diff --git a/Implab/AbstractPromise.cs b/Implab/AbstractPromise.cs --- a/Implab/AbstractPromise.cs +++ b/Implab/AbstractPromise.cs @@ -18,11 +18,13 @@ namespace Implab { 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 @@ namespace Implab { // 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 @@ namespace Implab { } 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 @@ namespace Implab { protected override Signal GetResolveSignal() { var signal = new Signal(); On(signal.Set, PromiseEventType.All); + return signal; } #endregion - public Type PromiseType { get { return typeof(void); diff --git a/Implab/AbstractPromiseT.cs b/Implab/AbstractPromiseT.cs --- a/Implab/AbstractPromiseT.cs +++ b/Implab/AbstractPromiseT.cs @@ -14,10 +14,14 @@ namespace Implab { m_success = success; m_error = error; m_cancel = cancel; + + m_handler = null; + m_mask = 0; } public HandlerDescriptor(Action success, Action error, Action cancel) { m_handler = success; + m_success = null; m_error = error; m_cancel = cancel; m_mask = PromiseEventType.Success; @@ -26,6 +30,9 @@ namespace Implab { 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 @@ namespace Implab { } 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 @@ namespace Implab { // 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 @@ namespace Implab { } 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 @@ namespace Implab { } } - - 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 On(Action success, Action error, Action cancel) { AddHandler(new HandlerDescriptor(success, error, cancel)); return this; @@ -146,6 +158,11 @@ namespace Implab { return this; } + IPromise IPromise.On(Action handler, PromiseEventType events) { + AddHandler(new HandlerDescriptor(handler, events)); + return this; + } + public IPromise Cast() { return (IPromise)this; } diff --git a/Implab/ActionChainTask.cs b/Implab/ActionChainTask.cs new file mode 100644 --- /dev/null +++ b/Implab/ActionChainTask.cs @@ -0,0 +1,23 @@ +using System; + +namespace Implab { + public class ActionChainTask : ActionChainTaskBase, IDeferred { + readonly Func m_task; + + public ActionChainTask(Func task, Func error, Func cancel) : base(error,cancel) { + m_task = task; + } + + public void Resolve() { + if (m_task != null && LockCancelation()) { + try { + Observe(m_task()); + } catch(Exception err) { + HandleErrorInternal(err); + } + } + } + + } +} + diff --git a/Implab/ActionChainTaskBase.cs b/Implab/ActionChainTaskBase.cs new file mode 100644 --- /dev/null +++ b/Implab/ActionChainTaskBase.cs @@ -0,0 +1,62 @@ +using System; +using System.Threading; + +namespace Implab { + public class ActionChainTaskBase : AbstractPromise { + readonly Func m_error; + readonly Func m_cancel; + + int m_cancelationLock; + + protected ActionChainTaskBase( Func error, Func 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); + } + } +} + diff --git a/Implab/ActionChainTaskT.cs b/Implab/ActionChainTaskT.cs new file mode 100644 --- /dev/null +++ b/Implab/ActionChainTaskT.cs @@ -0,0 +1,23 @@ +using System; + +namespace Implab { + public class ActionChainTask : ActionChainTaskBase, IDeferred { + readonly Func m_task; + + public ActionChainTask(Func task, Func error, Func 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); + } + } + } + + } +} + diff --git a/Implab/ActionTask.cs b/Implab/ActionTask.cs new file mode 100644 --- /dev/null +++ b/Implab/ActionTask.cs @@ -0,0 +1,22 @@ +using System; + +namespace Implab { + public class ActionTask : ActionTaskBase, IDeferred { + readonly Action m_task; + public ActionTask(Action task, Action error, Action cancel) : base(error,cancel) { + m_task = task; + } + + public void Resolve() { + if (m_task != null && LockCancelation()) { + try { + m_task(); + SetResult(); + } catch(Exception err) { + HandleErrorInternal(err); + } + } + } + } +} + diff --git a/Implab/ActionTaskBase.cs b/Implab/ActionTaskBase.cs new file mode 100644 --- /dev/null +++ b/Implab/ActionTaskBase.cs @@ -0,0 +1,55 @@ +using System; +using System.Threading; + +namespace Implab { + public class ActionTaskBase : AbstractPromise { + readonly Action m_cancel; + readonly Action m_error; + + int m_cancelationLock; + + protected ActionTaskBase( Action error, Action 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); + } + } +} + diff --git a/Implab/ActionTaskT.cs b/Implab/ActionTaskT.cs new file mode 100644 --- /dev/null +++ b/Implab/ActionTaskT.cs @@ -0,0 +1,22 @@ +using System; + +namespace Implab { + public class ActionTask : ActionTaskBase, IDeferred { + readonly Action m_task; + public ActionTask(Action task, Action error, Action 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); + } + } + } + } +} + diff --git a/Implab/ChainTask.cs b/Implab/ChainTask.cs deleted file mode 100644 --- a/Implab/ChainTask.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Threading; - -namespace Implab { - public class ChainTask : AbstractPromise, IDeferred { - readonly Func m_task; - readonly Action m_error; - readonly Action m_cancel; - - int m_cancelationLock; - - public ChainTask(Func task, Func error, Func 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); - } - } -} - diff --git a/Implab/FuncChainTask.cs b/Implab/FuncChainTask.cs new file mode 100644 --- /dev/null +++ b/Implab/FuncChainTask.cs @@ -0,0 +1,21 @@ +using System; + +namespace Implab { + public class FuncChainTask : FuncChainTaskBase, IDeferred { + readonly Func> m_task; + + public FuncChainTask(Func> task, Func> error, Func> 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 diff --git a/Implab/FuncChainTaskBase.cs b/Implab/FuncChainTaskBase.cs new file mode 100644 --- /dev/null +++ b/Implab/FuncChainTaskBase.cs @@ -0,0 +1,60 @@ +using System; +using System.Threading; + +namespace Implab { + public class FuncChainTaskBase : AbstractPromise { + readonly Func> m_error; + readonly Func> m_cancel; + + int m_cancelationLock; + + protected FuncChainTaskBase( Func> error, Func> 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); + } + } +} + diff --git a/Implab/FuncChainTaskT.cs b/Implab/FuncChainTaskT.cs new file mode 100644 --- /dev/null +++ b/Implab/FuncChainTaskT.cs @@ -0,0 +1,21 @@ +using System; + +namespace Implab { + public class FuncChainTask : FuncChainTaskBase, IDeferred { + readonly Func> m_task; + + public FuncChainTask(Func> task, Func> error, Func> 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 diff --git a/Implab/ICancelationToken.cs b/Implab/ICancelationToken.cs deleted file mode 100644 --- a/Implab/ICancelationToken.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; - -namespace Implab { - public interface ICancelationToken { - /// - /// Indicates wherther the cancellation was requested. - /// - bool IsCancelRequested { get ; } - - /// - /// The reason why the operation should be cancelled. - /// - Exception CancelReason { get ; } - - /// - /// Accepts if requested. - /// - /// true, if if requested was accepted, false otherwise. - bool AcceptIfRequested(); - - /// - /// Sets the token to cancelled state. - /// - /// The reason why the operation was cancelled. - void CancelOperation(Exception reason); - - /// - /// Adds the listener for the cancellation request, is the cancellation was requested the - /// is executed immediatelly. - /// - /// The handler which will be executed if the cancel occurs. - void CancelationRequested(Action handler); - - } -} - diff --git a/Implab/ICancellationToken.cs b/Implab/ICancellationToken.cs new file mode 100644 --- /dev/null +++ b/Implab/ICancellationToken.cs @@ -0,0 +1,36 @@ +using System; + +namespace Implab { + public interface ICancellationToken { + /// + /// Indicates wherther the cancellation was requested. + /// + bool IsCancellationRequested { get ; } + + /// + /// The reason why the operation should be cancelled. + /// + Exception CancellationReason { get ; } + + /// + /// Accepts if requested. + /// + /// true, if if requested was accepted, false otherwise. + bool CancelOperationIfRequested(); + + /// + /// Sets the token to cancelled state. + /// + /// The reason why the operation was cancelled. + void CancelOperation(Exception reason); + + /// + /// Adds the listener for the cancellation request, is the cancellation was requested the + /// is executed immediatelly. + /// + /// The handler which will be executed if the cancel occurs. + void CancellationRequested(Action handler); + + } +} + diff --git a/Implab/IDeferred.cs b/Implab/IDeferred.cs --- a/Implab/IDeferred.cs +++ b/Implab/IDeferred.cs @@ -4,7 +4,7 @@ namespace Implab { /// /// Deferred result, usually used by asynchronous services as the service part of the promise. /// - public interface IDeferred : ICancelationToken { + public interface IDeferred : ICancellationToken { void Resolve(); diff --git a/Implab/IDeferredT.cs b/Implab/IDeferredT.cs --- a/Implab/IDeferredT.cs +++ b/Implab/IDeferredT.cs @@ -1,7 +1,7 @@ using System; namespace Implab { - public interface IDeferred : ICancelationToken { + public interface IDeferred : ICancellationToken { void Resolve(T value); void Reject(Exception error); diff --git a/Implab/ITaskController.cs b/Implab/ITaskController.cs --- a/Implab/ITaskController.cs +++ b/Implab/ITaskController.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace Implab { public interface ITaskController: IProgressHandler, ICancellable { diff --git a/Implab/Implab.csproj b/Implab/Implab.csproj --- a/Implab/Implab.csproj +++ b/Implab/Implab.csproj @@ -157,14 +157,24 @@ - - + + + + + + + + + + + + diff --git a/Implab/Parallels/ArrayTraits.cs b/Implab/Parallels/ArrayTraits.cs --- a/Implab/Parallels/ArrayTraits.cs +++ b/Implab/Parallels/ArrayTraits.cs @@ -152,7 +152,7 @@ namespace Implab.Parallels { throw new ArgumentOutOfRangeException("threads","Threads number must be greater then zero"); if (source.Length == 0) - return Promise.ResultToPromise(new TDst[0]); + return Promise.FromResult(new TDst[0]); var promise = new Promise(); var res = new TDst[source.Length]; diff --git a/Implab/Parallels/AsyncPool.cs b/Implab/Parallels/AsyncPool.cs --- a/Implab/Parallels/AsyncPool.cs +++ b/Implab/Parallels/AsyncPool.cs @@ -31,6 +31,24 @@ namespace Implab.Parallels { return p; } + public static IPromise Invoke(Func func) { + var p = new Promise(); + 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 RunThread(Func func) { var p = new Promise(); @@ -52,6 +70,27 @@ namespace Implab.Parallels { return p; } + public static IPromise RunThread(Func func) { + var p = new Promise(); + + 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 @@ namespace Implab.Parallels { return p; } + public static IPromise RunThread(Action 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[] func) { + return func.Select(f => RunThread(f)).ToArray(); + } + public static IPromise[] RunThread(params Func[] func) { return func.Select(f => RunThread(f)).ToArray(); } + + public static IPromise[] RunThread(params Func[] func) { + return func.Select(f => RunThread(f)).ToArray(); + } } } diff --git a/Implab/PromiseExtensions.cs b/Implab/PromiseExtensions.cs --- a/Implab/PromiseExtensions.cs +++ b/Implab/PromiseExtensions.cs @@ -174,6 +174,116 @@ namespace Implab { return medium; } + + public static IPromise Then(this IPromise that, Action success, Action error, Action 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 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 Then(this IPromise that, Func success, Func error, Func cancel) { + Safe.ArgumentNotNull(that, "that"); + + var d = new FuncTask(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, Func success, Func error) { + return Then(that, success, error, null); + } + + public static IPromise Then(this IPromise that, Func success) { + return Then(that, success, null, null); + } + + public static IPromise Then(this IPromise that, Func success, Func error, Func cancel) { + Safe.ArgumentNotNull(that, "that"); + var d = new FuncTask(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, Func success, Func error) { + return Then(that, success, error, null); + } + + public static IPromise Then(this IPromise that, Func success) { + return Then(that, success, null, null); + } + + #region chain traits + public static IPromise Chain(this IPromise that, Func success, Func error, Func 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 success, Func error) { + return Chain(that, success, error, null); + } + + public static IPromise Chain(this IPromise that, Func success) { + return Chain(that, success, null, null); + } + + public static IPromise Chain(this IPromise that, Func> success, Func> error, Func> cancel) { + Safe.ArgumentNotNull(that, "that"); + + var d = new FuncChainTask(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> success, Func> error) { + return Chain(that, success, error, null); + } + + public static IPromise Chain(this IPromise that, Func> success) { + return Chain(that, success, null, null); + } + + public static IPromise Chain(this IPromise that, Func> success, Func> error, Func> cancel) { + Safe.ArgumentNotNull(that, "that"); + var d = new FuncChainTask(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> success, Func> error) { + return Chain(that, success, error, null); + } + + public static IPromise Chain(this IPromise that, Func> success) { + return Chain(that, success, null, null); + } + + #endregion + #if NET_4_5 diff --git a/Implab/Safe.cs b/Implab/Safe.cs --- a/Implab/Safe.cs +++ b/Implab/Safe.cs @@ -109,9 +109,9 @@ namespace Implab ArgumentNotNull(action, "action"); try { - return action() ?? Promise.ExceptionToPromise(new Exception("The action returned null")); + return action() ?? Promise.FromException(new Exception("The action returned null")); } catch (Exception err) { - return Promise.ExceptionToPromise(err); + return Promise.FromException(err); } } } diff --git a/Implab/SuccessPromise.cs b/Implab/SuccessPromise.cs new file mode 100644 --- /dev/null +++ b/Implab/SuccessPromise.cs @@ -0,0 +1,111 @@ +using System; + +namespace Implab { + public class SuccessPromise : IPromise { + #region IPromise implementation + + public IPromise On(Action success, Action error, Action 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 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 Cast() { + 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 + + } +} + diff --git a/Implab/SuccessPromiseT.cs b/Implab/SuccessPromiseT.cs new file mode 100644 --- /dev/null +++ b/Implab/SuccessPromiseT.cs @@ -0,0 +1,177 @@ +using System; + +namespace Implab { + public class SuccessPromise : IPromise { + readonly T m_value; + + public SuccessPromise(T value){ + m_value = value; + } + + public IPromise On(Action success, Action error, Action 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 On(Action success, Action 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 On(Action 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 On(Action success, Action error, Action 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 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; + } + + IPromise IPromise.On(Action success, Action error, Action cancel) { + return On(success, error, cancel); + } + + IPromise IPromise.On(Action success, Action 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 Cast() { + return new SuccessPromise((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) { + } + } +} + diff --git a/Implab/SyncContextPromise.cs b/Implab/SyncContextPromise.cs --- a/Implab/SyncContextPromise.cs +++ b/Implab/SyncContextPromise.cs @@ -10,15 +10,15 @@ namespace Implab { m_context = context; } - protected override void SignalSuccess(IDeferred handler) { + protected override void SignalSuccess(Promise.HandlerDescriptor handler) { m_context.Post(x => base.SignalSuccess(handler), null); } - protected override void SignalError(IDeferred handler, Exception error) { + protected override void SignalError(Promise.HandlerDescriptor handler, Exception error) { m_context.Post(x => base.SignalError(handler, error), null); } - protected override void SignalCancelled(IDeferred handler, Exception reason) { + protected override void SignalCancelled(Promise.HandlerDescriptor handler, Exception reason) { m_context.Post(x => base.SignalCancelled(handler, reason), null); } } diff --git a/MonoPlay/Program.cs b/MonoPlay/Program.cs --- a/MonoPlay/Program.cs +++ b/MonoPlay/Program.cs @@ -8,86 +8,33 @@ using System.Threading; 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(); - - 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(); + 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) { + + } } }