Fix timeout checking in FindFinalStatePosition (#66365)
authorStephen Toub <stoub@microsoft.com>
Thu, 10 Mar 2022 03:30:04 +0000 (22:30 -0500)
committerGitHub <noreply@github.com>
Thu, 10 Mar 2022 03:30:04 +0000 (20:30 -0700)
Exit the inner loop every now and then to do a timeout check.

src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Symbolic/SymbolicRegexMatcher.cs
src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.Match.Tests.cs

index 72cffa7..e96525c 100644 (file)
@@ -784,11 +784,17 @@ namespace System.Text.RegularExpressions.Symbolic
                         }
                     }
 
-                    // Now run the DFA or NFA traversal from the current point using the current state.
+                    // Now run the DFA or NFA traversal from the current point using the current state. If timeouts are being checked,
+                    // we need to pop out of the inner loop every now and then to do the timeout check in this outer loop.
+                    const int CharsPerTimeoutCheck = 10_000;
+                    ReadOnlySpan<char> inputForInnerLoop = _checkTimeout && input.Length - i > CharsPerTimeoutCheck ?
+                        input.Slice(0, i + CharsPerTimeoutCheck) :
+                        input;
+
                     int finalStatePosition;
                     int findResult = currentState.NfaState is not null ?
-                        FindFinalStatePositionDeltas<NfaStateHandler>(builder, input, ref i, ref currentState, ref matchLength, out finalStatePosition) :
-                        FindFinalStatePositionDeltas<DfaStateHandler>(builder, input, ref i, ref currentState, ref matchLength, out finalStatePosition);
+                        FindFinalStatePositionDeltas<NfaStateHandler>(builder, inputForInnerLoop, ref i, ref currentState, ref matchLength, out finalStatePosition) :
+                        FindFinalStatePositionDeltas<DfaStateHandler>(builder, inputForInnerLoop, ref i, ref currentState, ref matchLength, out finalStatePosition);
 
                     // If we reached a final or deadend state, we're done.
                     if (findResult > 0)
@@ -802,17 +808,20 @@ namespace System.Text.RegularExpressions.Symbolic
                     // find result will be 0, otherwise negative.
                     if (findResult < 0)
                     {
-                        if ((uint)i >= (uint)input.Length)
+                        if (i >= input.Length)
                         {
                             // We ran out of input. No match.
                             break;
                         }
 
-                        // We failed to transition. Upgrade to DFA mode.
-                        Debug.Assert(currentState.DfaState is not null);
-                        NfaMatchingState nfaState = perThreadData.NfaState;
-                        nfaState.InitializeFrom(currentState.DfaState);
-                        currentState = new CurrentState(nfaState);
+                        if (i < inputForInnerLoop.Length)
+                        {
+                            // We failed to transition. Upgrade to DFA mode.
+                            Debug.Assert(currentState.DfaState is not null);
+                            NfaMatchingState nfaState = perThreadData.NfaState;
+                            nfaState.InitializeFrom(currentState.DfaState);
+                            currentState = new CurrentState(nfaState);
+                        }
                     }
 
                     // Check for a timeout before continuing.
index 8bdcf61..d43057f 100644 (file)
@@ -1120,12 +1120,6 @@ namespace System.Text.RegularExpressions.Tests
         [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))]
         public async Task Match_Timeout_Repetition_Throws(RegexEngine engine)
         {
-            if (engine == RegexEngine.NonBacktracking)
-            {
-                // [ActiveIssue("https://github.com/dotnet/runtime/issues/65991")]
-                return;
-            }
-
             int repetitionCount = 800_000_000;
             Regex regex = await RegexHelpers.GetRegexAsync(engine, @"a\s{" + repetitionCount + "}", RegexOptions.None, TimeSpan.FromSeconds(1));
             string input = @"a" + new string(' ', repetitionCount) + @"b";