Procházet zdrojové kódy

Fix TimeSpan.Zero timeout execution order to maintain FIFO

Co-authored-by: tig <[email protected]>
copilot-swe-agent[bot] před 1 měsícem
rodič
revize
5b43a1147e

+ 18 - 8
Terminal.Gui/App/Timeout/TimedEvents.cs

@@ -133,24 +133,33 @@ public class TimedEvents : ITimedEvents
                 k -= 100;
             }
 
-            _timeouts.Add (NudgeToUniqueKey (k), timeout);
+            _timeouts.Add (NudgeToUniqueKey (k, time == TimeSpan.Zero), timeout);
             Added?.Invoke (this, new (timeout, k));
         }
     }
 
     /// <summary>
-    ///     Finds the closest number to <paramref name="k"/> that is not present in <see cref="_timeouts"/>
-    ///     (incrementally).
+    ///     Finds the closest number to <paramref name="k"/> that is not present in <see cref="_timeouts"/>.
+    ///     For immediate execution timeouts (decrementForImmediate=true), decrements to maintain FIFO order.
+    ///     For delayed timeouts, increments to maintain order.
     /// </summary>
-    /// <param name="k"></param>
-    /// <returns></returns>
-    private long NudgeToUniqueKey (long k)
+    /// <param name="k">The initial key to try</param>
+    /// <param name="decrementForImmediate">If true, decrements on collision; otherwise increments</param>
+    /// <returns>A unique key</returns>
+    private long NudgeToUniqueKey (long k, bool decrementForImmediate)
     {
         lock (_timeoutsLockToken)
         {
             while (_timeouts.ContainsKey (k))
             {
-                k++;
+                if (decrementForImmediate)
+                {
+                    k--;
+                }
+                else
+                {
+                    k++;
+                }
             }
         }
 
@@ -185,7 +194,8 @@ public class TimedEvents : ITimedEvents
             {
                 lock (_timeoutsLockToken)
                 {
-                    _timeouts.Add (NudgeToUniqueKey (k), timeout);
+                    // When re-adding non-executed timeouts, increment on collision to maintain original order
+                    _timeouts.Add (NudgeToUniqueKey (k, decrementForImmediate: false), timeout);
                 }
             }
         }

+ 29 - 0
Tests/UnitTests/Application/MainLoopTests.cs

@@ -661,6 +661,35 @@ public class MainLoopTests
 
         Application.Shutdown ();
     }
+
+    [Fact]
+    public void TimeSpanZero_Timeouts_Execute_In_FIFO_Order ()
+    {
+        var ml = new MainLoop (new FakeMainLoop ());
+        var executionOrder = new List<int> ();
+
+        // Add multiple timeouts with TimeSpan.Zero in quick succession
+        // They should execute in the order they were added (FIFO)
+        for (int i = 0; i < 10; i++)
+        {
+            int capturedI = i;
+            ml.TimedEvents.Add (TimeSpan.Zero, () =>
+                                                {
+                                                    executionOrder.Add (capturedI);
+                                                    return false; // Don't repeat
+                                                });
+        }
+
+        // Run one iteration to execute all pending timeouts
+        ml.RunIteration ();
+
+        // Verify they executed in FIFO order (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
+        Assert.Equal (10, executionOrder.Count);
+        for (int i = 0; i < 10; i++)
+        {
+            Assert.Equal (i, executionOrder [i]);
+        }
+    }
     
     [Fact]
     public void RemoveIdle_Function_NotCalled ()