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 @@ -1,16 +1,16 @@ - net46 - /usr/lib/mono/4.5/ + netcoreapp2.1 false + - - + + diff --git a/Implab.Test/RunnableComponentTests.cs b/Implab.Test/RunnableComponentTests.cs new file mode 100644 --- /dev/null +++ b/Implab.Test/RunnableComponentTests.cs @@ -0,0 +1,42 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Implab.Components; +using Xunit; + +namespace Implab.Test +{ + class TimeLog : PollingComponent { + public TimeLog() : base(true) { + } + + protected override Task Poll(CancellationToken ct) { + Console.WriteLine("Poll"); + return Task.CompletedTask; + } + } + + public class UnitTest1 + { + [Fact] + public async Task Test1() + { + + using(var tl = new TimeLog()) { + tl.StateChanged += (self, args) => Console.WriteLine("{0}", args.State); + tl.Delay = 1000; + tl.Interval = 500; + + + tl.Start(CancellationToken.None); + await tl.Completion; + + await Task.Delay(2000); + + tl.Stop(CancellationToken.None); + await tl.Completion; + await Task.Delay(3000); + } + } + } +} diff --git a/Implab.Test/UnitTest1.cs b/Implab.Test/UnitTest1.cs deleted file mode 100644 --- a/Implab.Test/UnitTest1.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Diagnostics; -using System.Threading; -using Implab.Diagnostics; -using System.Linq; -using Xunit; - -namespace Implab.Test { - using System.Threading.Tasks; - using static Trace; - public class UnitTest1 { - [Fact] - public async Task Test1() { - var listener = new SimpleTraceListener(Console.Out); - listener.TraceOutputOptions |= TraceOptions.ThreadId; - - var source = TraceSource; - source.Switch.Level = SourceLevels.All; - - source.Listeners.Add(listener); - - using (LogicalOperation("Test1")){ - await Task.Yield(); - Log(String.Join(", ", Trace.CorrelationManager.LogicalOperationStack.Cast().Select(x => x.ToString()))); - await AsyncDummy(); - Log(String.Join(", ", Trace.CorrelationManager.LogicalOperationStack.Cast().Select(x => x.ToString()))); - } - } - - async Task AsyncDummy() { - using(LogicalOperation("OuterDummy")) - using(LogicalOperation("InnerDummy")) { - Log(String.Join(", ", Trace.CorrelationManager.LogicalOperationStack.Cast().Select(x => x.ToString()))); - await Task.Delay(1); - Log(String.Join(", ", Trace.CorrelationManager.LogicalOperationStack.Cast().Select(x => x.ToString()))); - } - Log(String.Join(", ", Trace.CorrelationManager.LogicalOperationStack.Cast().Select(x => x.ToString()))); - } - } -} diff --git a/Implab/Components/PollingComponent.cs b/Implab/Components/PollingComponent.cs --- a/Implab/Components/PollingComponent.cs +++ b/Implab/Components/PollingComponent.cs @@ -9,6 +9,9 @@ namespace Implab.Components { readonly CancellationTokenSource m_cancellation = new CancellationTokenSource(); + Task m_pending; + Task m_poll; + /// /// Poll interval in milliseconds. /// @@ -41,20 +44,33 @@ namespace Implab.Components { } - //TODO override stop + protected override async Task StopInternalAsync(CancellationToken ct) { + // component in Stopping state, no new polls will be scheduled + m_cancellation.Cancel(); + try { + // await for pending poll + await m_poll; + } catch (OperationCanceledException e) { + // OK + } + } protected abstract Task Poll(CancellationToken ct); void ScheduleNextPoll(int timeout) { lock (SynchronizationObject) { - if (State == ExecutionState.Running) + if (State == ExecutionState.Running) { + m_pending = Safe.CreateTask(m_cancellation.Token); + m_poll = m_pending.Then(() => Poll(m_cancellation.Token)); m_timer.Change(timeout, Timeout.Infinite); + } } } - void OnTimer(object state) { + async void OnTimer(object state) { try { - Poll(m_cancellation.Token); + m_pending.Start(); + await m_poll; } catch (Exception e) { UnhandledException.DispatchEvent(this, new UnhandledExceptionEventArgs(e, false)); if (FailOnError) diff --git a/Implab/PromiseExtensions.cs b/Implab/PromiseExtensions.cs --- a/Implab/PromiseExtensions.cs +++ b/Implab/PromiseExtensions.cs @@ -211,6 +211,15 @@ namespace Implab { })); } + public static PromiseAwaiter GetAwaiter(this IPromise that) { + Safe.ArgumentNotNull(that, nameof(that)); + return new PromiseAwaiter(that); + } + + public static PromiseAwaiter GetAwaiter(this IPromise that) { + Safe.ArgumentNotNull(that, nameof(that)); + return new PromiseAwaiter(that); + } } } diff --git a/Implab/Safe.cs b/Implab/Safe.cs --- a/Implab/Safe.cs +++ b/Implab/Safe.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Collections; using System.Runtime.CompilerServices; using System.Threading.Tasks; +using System.Threading; #if NET_4_5 using System.Threading.Tasks; @@ -151,6 +152,21 @@ namespace Implab public static void NoWait(Task promise) { } + public static void Noop() { + } + + public static void Noop(CancellationToken ct) { + ct.ThrowIfCancellationRequested(); + } + + public static Task CreateTask() { + return new Task(Noop); + } + + public static Task CreateTask(CancellationToken ct) { + return new Task(Noop, ct); + } + [DebuggerStepThrough] public static IPromise Run(Func> action) { ArgumentNotNull(action, "action"); @@ -162,10 +178,5 @@ namespace Implab } } -#if NET_4_5 - public static void NoWait(Task t) { - } -#endif - } }