diff --git a/Implab.Test/Mock/MockRunnableComponent.cs b/Implab.Test/Mock/MockRunnableComponent.cs --- a/Implab.Test/Mock/MockRunnableComponent.cs +++ b/Implab.Test/Mock/MockRunnableComponent.cs @@ -24,7 +24,7 @@ namespace Implab.Test.Mock { set; } - public Action MockDispose { + public Action MockDispose { get; set; } @@ -42,10 +42,10 @@ namespace Implab.Test.Mock { MockInit(); } - protected override void Dispose(bool disposing, Exception lastError) { + protected override void Dispose(bool disposing) { if (MockDispose != null) - MockDispose(disposing, lastError); - base.Dispose(disposing, lastError); + MockDispose(disposing); + base.Dispose(disposing); } } } diff --git a/Implab.Test/RunnableComponentTests.cs b/Implab.Test/RunnableComponentTests.cs --- a/Implab.Test/RunnableComponentTests.cs +++ b/Implab.Test/RunnableComponentTests.cs @@ -95,7 +95,7 @@ namespace Implab.Test { var comp = new MockRunnableComponent(true); bool disposed = false; - comp.MockDispose = (disposing, error) => { + comp.MockDispose = (disposing) => { disposed = true; }; @@ -115,7 +115,7 @@ namespace Implab.Test { var comp = new MockRunnableComponent(true, true); bool disposed = false; - comp.MockDispose = (disposing, error) => { + comp.MockDispose = (disposing) => { disposed = true; }; @@ -131,10 +131,8 @@ namespace Implab.Test { var comp = new MockRunnableComponent(true, true); bool disposed = false; - Exception lastError = null; - comp.MockDispose = (disposing, error) => { + comp.MockDispose = (disposing) => { disposed = true; - lastError = error; }; comp.Start().Join(1000); diff --git a/Implab/Components/Disposable.cs b/Implab/Components/Disposable.cs --- a/Implab/Components/Disposable.cs +++ b/Implab/Components/Disposable.cs @@ -1,5 +1,6 @@ using Implab.Diagnostics; using System; +using System.Diagnostics.CodeAnalysis; using System.Threading; namespace Implab.Components { @@ -81,6 +82,7 @@ namespace Implab.Components { } } + [SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "Dipose(bool) and GC.SuppessFinalize are called")] public void Dispose() { if (Interlocked.Increment(ref m_disposed) == 1) { Dispose(true); diff --git a/Implab/Components/IRunnable.cs b/Implab/Components/IRunnable.cs --- a/Implab/Components/IRunnable.cs +++ b/Implab/Components/IRunnable.cs @@ -1,14 +1,22 @@ using System; namespace Implab.Components { - public interface IRunnable { + /// + /// Interface for the component which performs a long running task. + /// + /// + /// The component also should implement interface to be able to release used resources. + /// All methods of this interface must be a thread safe. If the operation is not applicable in the current state the + /// method should throw an exception and keep the current state unchanged. + /// + public interface IRunnable : IDisposable { /// - /// Starts this instance. + /// Starts this instance /// IPromise Start(); /// - /// Stops this instance. After the instance is stopped it can't be started again, stopping should be treated as gracefull and async dispose. + /// Stops this instance, after the instance is stopped it can move to Failed, Ready or Disposed state, in case with the last it can't be reused. /// IPromise Stop(); diff --git a/Implab/Components/PollingComponent.cs b/Implab/Components/PollingComponent.cs --- a/Implab/Components/PollingComponent.cs +++ b/Implab/Components/PollingComponent.cs @@ -144,11 +144,11 @@ namespace Implab.Components { return base.OnStop(); } - protected override void Dispose(bool disposing, Exception lastError) { + protected override void Dispose(bool disposing) { if (disposing) - Safe.Dispose(m_timer); + m_timer.Dispose(); - base.Dispose(disposing, lastError); + base.Dispose(disposing); } } } diff --git a/Implab/Components/RunnableComponent.cs b/Implab/Components/RunnableComponent.cs --- a/Implab/Components/RunnableComponent.cs +++ b/Implab/Components/RunnableComponent.cs @@ -1,5 +1,6 @@ using System; - +using System.Diagnostics.CodeAnalysis; + namespace Implab.Components { public abstract class RunnableComponent : IDisposable, IRunnable, IInitializable { enum Commands { @@ -333,30 +334,22 @@ namespace Implab.Components { /// especially if method is failed. Using this method insted of may /// lead to the data loss by the component. /// + [SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "Dipose(bool) and GC.SuppessFinalize are called")] public void Dispose() { IPromise pending; lock (m_stateMachine) { if (m_stateMachine.State == ExecutionState.Disposed) return; - pending = Move(Commands.Dispose, null, null); + Move(Commands.Dispose, null, null); } GC.SuppressFinalize(this); - if (pending != null) { - pending.Cancel(); - pending.Timeout(DisposeTimeout).On( - () => Dispose(true, null), - err => Dispose(true, err), - reason => Dispose(true, new OperationCanceledException("The operation is cancelled", reason)) - ); - } else { - Dispose(true, null); - } + Dispose(true); } ~RunnableComponent() { - Dispose(false, null); + Dispose(false); } #endregion @@ -365,8 +358,8 @@ namespace Implab.Components { /// Releases all resources used by the component, called automatically, override this method to implement your cleanup. /// /// true if this method is called during normal dispose process. - /// The last error which occured during the component stop. - protected virtual void Dispose(bool disposing, Exception lastError) { + /// The operation which is currenty pending + protected virtual void Dispose(bool disposing) { } diff --git a/Implab/Formats/JSON/JSONParser.cs b/Implab/Formats/JSON/JSONParser.cs --- a/Implab/Formats/JSON/JSONParser.cs +++ b/Implab/Formats/JSON/JSONParser.cs @@ -274,7 +274,7 @@ namespace Implab.Formats.JSON { protected override void Dispose(bool disposing) { if (disposing) - Safe.Dispose(m_scanner); + m_scanner.Dispose(); } /// diff --git a/Implab/Formats/JSON/JSONScanner.cs b/Implab/Formats/JSON/JSONScanner.cs --- a/Implab/Formats/JSON/JSONScanner.cs +++ b/Implab/Formats/JSON/JSONScanner.cs @@ -102,7 +102,7 @@ namespace Implab.Formats.JSON { protected override void Dispose(bool disposing) { if (disposing) - Safe.Dispose(m_scanner); + m_scanner.Dispose(); base.Dispose(disposing); } } diff --git a/Implab/PromiseExtensions.cs b/Implab/PromiseExtensions.cs --- a/Implab/PromiseExtensions.cs +++ b/Implab/PromiseExtensions.cs @@ -120,8 +120,12 @@ namespace Implab { } public static IPromise PromiseAll(this IEnumerable> that) { + return PromiseAll(that, null); + } + + public static IPromise PromiseAll(this IEnumerable> that, Action cleanup) { Safe.ArgumentNotNull(that, "that"); - return PromiseAll(that.ToList()); + return PromiseAll(that.ToList(), cleanup); } public static IPromise PromiseAll(this ICollection that) { @@ -164,17 +168,35 @@ namespace Implab { return medium; } - public static IPromise PromiseAll(this ICollection> that) { + public static IPromise PromiseAll(this ICollection> that) { + return PromiseAll(that, null); + } + + /// + /// Creates a new promise which will be satisfied when all promises are satisfied. + /// + /// + /// + /// A callback used to cleanup already resolved promises in case of an error + /// + public static IPromise PromiseAll(this ICollection> that, Action cleanup) { Safe.ArgumentNotNull(that, "that"); + + int count = that.Count; - int count = that.Count; + if (count == 0) + return Promise.FromResult(new T[0]); + int errors = 0; var medium = new Promise(); var results = new T[that.Count]; medium.On(() => { - foreach (var p2 in that) - p2.Cancel(); + foreach (var p2 in that) { + p2.Cancel(); + if (cleanup != null) + p2.On(cleanup); + } }, PromiseEventType.ErrorOrCancel); int i = 0; @@ -414,6 +436,12 @@ namespace Implab { return new PromiseAwaiter(that); } + public static PromiseAwaiter GetAwaiter(this IPromise that) { + Safe.ArgumentNotNull(that, "that"); + + return new PromiseAwaiter(that); + } + #endif } }