Bladeren bron

PsQueue add TimeoutUtils, fix PsProcessProviderTest timeouts

Dalibor Votruba 1 jaar geleden
bovenliggende
commit
3e146d6bd8

+ 1 - 1
qdr.fnd.core.pqueue.test/Process/PsProcessProviderBaseTest.cs

@@ -136,7 +136,7 @@ namespace qdr.fnd.core.pqueue.test.Process
             Assert.That(process.IsRunning, Is.False);
             Assert.That((DateTime.Now - dtStart).TotalSeconds, Is.LessThan(2), "Timeout restriction doesn't work.");
             Assert.That(_statusDonePool.ContainsKey(process.ItemId), Is.True);
-            Assert.That(_statusDonePool[process.ItemId].Any(x => x.Item1 == PsStatusEnum.DoneOk), Is.True);
+            Assert.That(_statusDonePool[process.ItemId].Any(x => x.Item1 == PsStatusEnum.DoneFailed), Is.True);
         }
 
         private PsProcessProviderBaseMock CreateProcess(TypedArgument[] args)

+ 34 - 10
qdr.fnd.core.pqueue/Process/PsProcessProviderBase.cs

@@ -64,7 +64,7 @@ namespace qdr.fnd.core.pqueue.Process
             OnValidate();
         }
         /// <inheritdoc />
-        public void Start()
+        public async void Start()
         {
             if (IsRunning)
                 throw new PsProcessProviderException(PsProcessProviderException.ErrorCodes.ProcessProviderIsRunning, GetType().Name, ItemId);
@@ -73,15 +73,39 @@ namespace qdr.fnd.core.pqueue.Process
             IsRunning = true;
             var timeout = GetArgumentValue<TimeSpan>(CS_ARG_PROCESS_TIMEOUT);
 
-            _ = Task.Factory.StartNew(() => StartInternal(timeout).TimeoutAfter(_cancelationToken!, timeout), _cancelationToken!.Token);
+            var dtStart = DateTime.Now;
+            try
+            {
+                await TimeoutUtils.ExecuteWithTimeoutAsync(async () =>
+                {
+
+                    await OnProcess().ContinueWith(task =>
+                    {
+                        Log.Log(LogSeverityEnum.Info, $"Provider for ItemId='{ItemId}' done. Duration {(DateTime.Now - dtStart).ToReadableString()}.");
+                        if (ProcessDoneCallback != null)
+                            ProcessDoneCallback(ItemId, PsStatusEnum.DoneOk, "Succesfuly done.");
+                        IsRunning = false;
+                    });
+                }, timeout);
+            }
+            catch (Exception e)
+            {
+                await OnProcessError(e).ContinueWith(task =>
+                {
+                    Log.Log(LogSeverityEnum.Error, $"Provider running failed. Duration {(DateTime.Now - dtStart).ToReadableString()}.", e);
+                    if (ProcessDoneCallback != null)
+                        ProcessDoneCallback(ItemId, PsStatusEnum.DoneFailed, e.Message);
+                    IsRunning = false;
+                });
+            }
         }
 
         /// <inheritdoc />
         public void Stop()
         {
             if (!IsRunning)
-            throw new PsProcessProviderException(PsProcessProviderException.ErrorCodes.ProcessProviderNotRunning, GetType().Name, ItemId);
-   
+                throw new PsProcessProviderException(PsProcessProviderException.ErrorCodes.ProcessProviderNotRunning, GetType().Name, ItemId);
+
             _cancelationToken?.Cancel();
             IsRunning = false;
         }
@@ -104,7 +128,7 @@ namespace qdr.fnd.core.pqueue.Process
         /// <inheritdoc />
         public void WaitToDone()
         {
-            while(IsRunning)
+            while (IsRunning)
             {
                 Task.Delay(100).Wait();
             };
@@ -164,13 +188,13 @@ namespace qdr.fnd.core.pqueue.Process
             ProcessStdOutputCallback = null!;
         }
 
-        
-        private async Task StartInternal(TimeSpan timeout)
+
+        private async Task StartInternal()
         {
             var dtStart = DateTime.Now;
             try
             {
-                Log.Log(LogSeverityEnum.Debug, $"Provider for ItemId='{ItemId}' has general timeout for execution {timeout.ToReadableString()}.");
+
                 await OnProcess().ContinueWith(task =>
                 {
                     Log.Log(LogSeverityEnum.Info, $"Provider for ItemId='{ItemId}' done. Duration {(DateTime.Now - dtStart).ToReadableString()}.");
@@ -178,7 +202,7 @@ namespace qdr.fnd.core.pqueue.Process
                         ProcessDoneCallback(ItemId, PsStatusEnum.DoneOk, "Succesfuly done.");
                     IsRunning = false;
                 });
-                
+
             }
             catch (Exception e)
             {
@@ -192,7 +216,7 @@ namespace qdr.fnd.core.pqueue.Process
             }
         }
 
-        
+
 
         #endregion
 

+ 30 - 0
qdr.fnd.core.pqueue/fnd.candidates/TimeoutUtils.cs

@@ -0,0 +1,30 @@
+namespace qdr.fnd.core.pqueue.fnd.candidates
+{
+    public class TimeoutUtils
+    {
+        public static async Task ExecuteWithTimeoutAsync(Action action, TimeSpan timeout)
+        {
+            using var cts = new CancellationTokenSource();
+            var task = Task.Run(action, cts.Token);
+
+            try
+            {
+                if (await Task.WhenAny(task, Task.Delay(timeout, cts.Token)) == task)
+                {
+                    // Task completed within the timeout
+                    await task; // Propagate any exceptions
+                }
+                else
+                {
+                    // Timeout occurred
+                    cts.Cancel();
+                    throw new TimeoutException($"The operation has timed out after {timeout.TotalSeconds} seconds.");
+                }
+            }
+            finally
+            {
+                cts.Cancel(); // Ensure we always cancel the CancellationTokenSource
+            }
+        }
+    }
+}