Copilot commented on code in PR #414:
URL: https://github.com/apache/commons-jexl/pull/414#discussion_r3894352803


##########
src/main/java/org/apache/commons/jexl3/internal/Interpreter.java:
##########
@@ -1377,12 +1378,47 @@ protected Object visit(final ASTEQSNode node, final 
Object data) {
     @Override
     protected Object visit(final ASTERNode node, final Object data) {
         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
-        final Object right = node.jjtGetChild(1).jjtAccept(this, data);
+        final JexlNode rightNode = node.jjtGetChild(1);
+        final Object right = resolvePattern(rightNode, 
rightNode.jjtAccept(this, data));
         // note the arguments inversion between 'in'/'matches' and 'contains'
         // if x in y then y contains x
         return operators.contains(node, JexlOperator.CONTAINS, right, left);
     }
 
+    /**
+     * If the right operand of {@code =~} / {@code !~} is a string literal, 
compile it to a Pattern once and
+     * cache the result in the node's value slot (same mechanism as negated 
numeric literals).
+     * Dynamic string values (from variables) are returned unchanged.
+     * The regex string length is validated before compilation (JEXL-security 
f012).
+     * Uses double-check locking: first check without lock, then synchronized 
recheck-and-set to avoid
+     * holding the lock during expensive Pattern.compile() when the same 
compiled script runs concurrently.
+     */
+    private static Object resolvePattern(final JexlNode rightNode, final 
Object right) {
+        if (right instanceof CharSequence && rightNode instanceof 
JexlNode.Constant) {
+            // First check (volatile read, no lock)
+            Object cached = rightNode.jjtGetValue();
+            if (cached instanceof Pattern) {
+                return cached;
+            }
+            // Compile without holding lock
+            final String regex = right.toString();
+            if (regex.length() > JexlArithmetic.REGEX_PATTERN_MAX_LENGTH) {
+                throw new ArithmeticException("regular expression too long: " 
+ regex.length()
+                    + " > " + JexlArithmetic.REGEX_PATTERN_MAX_LENGTH);
+            }
+            final Pattern compiled = Pattern.compile(regex);
+            // Double-check and set under lock
+            synchronized (rightNode) {
+                cached = rightNode.jjtGetValue();
+                if (!(cached instanceof Pattern)) {
+                    rightNode.jjtSetValue(compiled);
+                }
+                return cached instanceof Pattern ? cached : compiled;
+            }
+        }
+        return right;
+    }

Review Comment:
   The comment claims a “volatile read” but `rightNode.jjtGetValue()` is not 
guaranteed to provide volatile/visibility semantics. Since the cached `Pattern` 
is written under `synchronized (rightNode)` but read outside any 
synchronization, this is not a correct double-check pattern and can lead to 
repeated compilation and (depending on JexlNode implementation) unsafe 
publication concerns. Consider using a thread-safe publication mechanism for 
the cached value (e.g., store an `AtomicReference<Pattern>` in the node value, 
or ensure both read+write are guarded consistently by the same lock, or use a 
dedicated concurrent cache keyed by node identity).



##########
src/main/java/org/apache/commons/jexl3/JexlArithmetic.java:
##########
@@ -2435,4 +2493,42 @@ public Object xor(final Object left, final Object right) 
{
         final long r = toLong(right);
         return l ^ r;
     }
+
+    /**
+     * A CharSequence wrapper that throws ArithmeticException if the current 
thread is interrupted.
+     * Used as the input to {@code Pattern.matcher()} so that 
catastrophic-backtracking regex matches
+     * remain responsive to JEXL cancellation (which sets the thread interrupt 
flag).
+     * The interrupt flag is checked every 256 {@code charAt} calls to limit 
overhead.
+     */
+    private static final class InterruptibleCharSequence implements 
CharSequence {
+        private final String seq;
+        private int count;
+
+        private InterruptibleCharSequence(final String s) {
+            this.seq = s;
+        }
+
+        @Override
+        public char charAt(final int index) {
+            if ((++count & 0xff) == 0 && 
Thread.currentThread().isInterrupted()) {
+                throw new ArithmeticException("Operation interrupted");
+            }
+            return seq.charAt(index);
+        }
+
+        @Override
+        public int length() {
+            return seq.length();
+        }
+
+        @Override
+        public CharSequence subSequence(final int start, final int end) {
+            return seq.subSequence(start, end);

Review Comment:
   `InterruptibleCharSequence.subSequence()` returns the underlying `String` 
subsequence, which does not perform the interruption checks. If the regex 
engine ends up operating on a subsequence (directly or indirectly), 
interruption responsiveness can be bypassed. To preserve the guarantee, return 
an interruptible view for subsequences as well (e.g., a new wrapper over the 
substring / a view that offsets indices) so that `charAt()` always funnels 
through the interrupt check.



##########
src/main/java/org/apache/commons/jexl3/JexlArithmetic.java:
##########
@@ -908,6 +934,28 @@ public boolean equals(final Object left, final Object 
right) {
         return compare(left, right, EQ) == 0;
     }
 
+    /**
+     * Guards a BigInteger result against exceeding the arithmetic context's 
precision (JEXL-security f013).
+     * <p>When {@link MathContext#getPrecision()} is zero (unlimited), no 
limit is enforced.
+     * Otherwise, the BigInteger must fit within approximately that many 
significant decimal digits.</p>
+     *
+     * @param big the value to check
+     * @return big unchanged if within the limit
+     * @throws ArithmeticException when the limit is exceeded
+     */
+    protected BigInteger checkBigIntegerPrecision(final BigInteger big) {
+        final int precision = getMathContext().getPrecision();
+        if (precision > 0) {
+            // precision 0 means unlimited; otherwise, one decimal digit ≈ 
log2(10) ≈ 10/3 bits
+            final int maxBits = precision * 10 / 3 + 1;
+            if (big.bitLength() > maxBits) {
+                throw new ArithmeticException(
+                  "BigInteger precision exceeded: " + big.bitLength() + " bits 
for " + precision + "-digit context");
+            }

Review Comment:
   `precision * 10 / 3 + 1` is computed in `int` and can overflow for large 
`MathContext` precision values, producing an incorrect (possibly negative) bit 
limit and breaking the guard. Use `long` (or `Math.multiplyExact`) for the 
calculation and clamp to a sensible upper bound before comparing against 
`bitLength()`; apply the same treatment anywhere this formula is repeated 
(including multiply pre-check and NumberParser digit cap).



##########
src/test/java/org/apache/commons/jexl3/ArithmeticTest.java:
##########
@@ -2409,4 +2409,101 @@ void setOptions(final JexlOptions options) {
         assertEquals("zero", jexl.createExpression("array.0").evaluate(jc));
         assertEquals("one", jexl.createExpression("array.1").evaluate(jc));
     }
+
+    // ----- security fixes f012 / f013 / f014 -----
+
+    /**
+     * f014: BigInteger literal with more digits than MAX_BIGINTEGER_DIGITS 
must be rejected at parse time.
+     */
+    @Test
+    void testBigIntegerLiteralTooLong() {
+        // normal H-literal still works
+        assertNotNull(JEXL.createScript("42H"));
+        // a literal just over the cap (256 + 1 digits + 'H') must fail to 
parse
+        final char[] digits = new char[256 + 1];
+        java.util.Arrays.fill(digits, '1');
+        final String huge = new String(digits) + "H";
+        assertThrows(JexlException.Parsing.class, () -> 
JEXL.createScript(huge));
+    }
+
+    /**
+     * f013: BigInteger arithmetic results that exceed the MathContext 
precision must throw.
+     */
+    @Test
+    void testBigIntegerArithmeticPrecisionCap() {
+        // precision=3 caps at ~11 bits (formula: 3 * 10 / 3 + 1 = 11), so 
results > 2047 are rejected
+        final JexlArithmetic bounded = new JexlArithmetic(true, new 
MathContext(3), JexlArithmetic.BIGD_SCALE);
+        final JexlEngine jexl = new JexlBuilder().arithmetic(bounded).create();
+        // small values are fine
+        assertEquals(new BigInteger("3"), jexl.createScript("a + b", "a", "b")
+                .execute(null, BigInteger.ONE, BigInteger.valueOf(2L)));
+        // result > 2047 is rejected: 1500 + 1000 = 2500, bitLength=12 > 11
+        assertThrows(JexlException.class, () ->
+                jexl.createScript("a + b", "a", "b")
+                    .execute(null, BigInteger.valueOf(1500L), 
BigInteger.valueOf(1000L)));
+        // multiply pre-check: 64 (7 bits) * 64 (7 bits), sum of operand bits 
= 14 > 11
+        assertThrows(JexlException.class, () ->
+                jexl.createScript("a * b", "a", "b")
+                    .execute(null, BigInteger.valueOf(64L), 
BigInteger.valueOf(64L)));
+    }
+
+    /**
+     * f012: a regex pattern string longer than REGEX_PATTERN_MAX_LENGTH must 
throw.
+     */
+    @Test
+    void testRegexPatternTooLong() {
+        final JexlEngine jexl = new JexlBuilder().strict(true).create();
+        final JexlScript script = jexl.createScript("x =~ y", "x", "y");
+        final char[] chars = new char[JexlArithmetic.REGEX_PATTERN_MAX_LENGTH 
+ 1];
+        java.util.Arrays.fill(chars, 'a');
+        final String longPattern = new String(chars);
+        assertThrows(JexlException.class, () -> script.execute(null, "abc", 
longPattern));
+    }
+
+    /**
+     * f012: regex matching must respond to thread interruption so a 
catastrophic-backtracking
+     * pattern does not hang a cancellable engine indefinitely.
+     */
+    @Test
+    void testRegexMatchingInterruptible() throws InterruptedException {
+        final JexlEngine jexl = new JexlBuilder().cancellable(true).create();
+        final JexlScript script = jexl.createScript("x =~ y", "x", "y");
+        // Catastrophic backtracking pattern on non-matching input to force 
long character scanning
+        final String evilPattern = "(a+)+b";
+        // Use a larger set of 'a's to extend matching time
+        final char[] chars = new char[50];
+        java.util.Arrays.fill(chars, 'a');
+        final String evilValue = new String(chars) + "c";
+
+        final java.util.concurrent.atomic.AtomicReference<Exception> caught =
+            new java.util.concurrent.atomic.AtomicReference<>();
+        final java.util.concurrent.CountDownLatch started = new 
java.util.concurrent.CountDownLatch(1);
+
+        final Thread t = new Thread(() -> {
+            try {
+                started.countDown();
+                script.execute(null, evilValue, evilPattern);
+            } catch (final Exception e) {
+                caught.set(e);
+            }
+        });
+
+        t.start();
+        // Wait for thread to actually start executing
+        started.await();
+        // Give regex matching time to engage (50 'a's with (a+)+b pattern 
causes backtracking)
+        Thread.sleep(300);
+        // Interrupt the matching thread
+        t.interrupt();
+        // Wait for thread to complete (should exit promptly if 
InterruptibleCharSequence is working)
+        t.join(5000);
+
+        assertFalse(t.isAlive(), "Thread should have completed after 
interruption (regex should be interruptible)");
+        // The thread may complete without exception if the regex finishes 
faster than interruption catches it,
+        // or it may throw Cancel if interrupted during charset access. Both 
are acceptable here.
+        if (caught.get() != null) {
+            assertTrue(caught.get() instanceof JexlException.Cancel,
+                "If interrupted during matching, expected 
JexlException.Cancel, got " + caught.get().getClass().getSimpleName());
+        }

Review Comment:
   This test is timing-dependent and can pass even if interruptibility is 
broken (e.g., if the regex finishes before the interrupt, `caught` remains null 
and the test still succeeds). To make this deterministic, prefer asserting that 
an interrupt during matching reliably triggers `JexlException.Cancel` (or at 
least that an interrupt results in a failure outcome), and avoid fixed sleeps 
by using a stronger synchronization signal (e.g., larger input + polling until 
the worker thread is inside execution, or repeat until you observe the 
cancellation path within a bounded overall timeout).



##########
src/main/java/org/apache/commons/jexl3/parser/Parser.jjt:
##########
@@ -103,6 +103,10 @@ public final class Parser extends JexlParser
             JexlInfo ji = et == null ? info : info.at(et.beginLine, 
et.beginColumn);
             String msg = et == null ? xparse.getMessage() : et.image;
             throw new JexlException.Parsing(ji, msg).clean();
+        } catch (NumberFormatException xnfe) {
+            Token et = errorToken(jj_lastpos, jj_scanpos, token.next, token);
+            JexlInfo ji = et == null ? info : info.at(et.beginLine, 
et.beginColumn);
+            throw new JexlException.Parsing(ji, xnfe.getMessage()).clean();

Review Comment:
   `NumberFormatException.getMessage()` can be null or overly 
low-level/implementation-specific. For parse errors, it’s better to ensure a 
stable, user-facing message (e.g., using the token image when available, and 
falling back to a generic 'invalid number literal' message when the exception 
message is null).



##########
src/test/java/org/apache/commons/jexl3/ArithmeticTest.java:
##########
@@ -2409,4 +2409,101 @@ void setOptions(final JexlOptions options) {
         assertEquals("zero", jexl.createExpression("array.0").evaluate(jc));
         assertEquals("one", jexl.createExpression("array.1").evaluate(jc));
     }
+
+    // ----- security fixes f012 / f013 / f014 -----
+
+    /**
+     * f014: BigInteger literal with more digits than MAX_BIGINTEGER_DIGITS 
must be rejected at parse time.
+     */
+    @Test
+    void testBigIntegerLiteralTooLong() {
+        // normal H-literal still works
+        assertNotNull(JEXL.createScript("42H"));
+        // a literal just over the cap (256 + 1 digits + 'H') must fail to 
parse
+        final char[] digits = new char[256 + 1];
+        java.util.Arrays.fill(digits, '1');
+        final String huge = new String(digits) + "H";
+        assertThrows(JexlException.Parsing.class, () -> 
JEXL.createScript(huge));
+    }
+
+    /**
+     * f013: BigInteger arithmetic results that exceed the MathContext 
precision must throw.
+     */
+    @Test
+    void testBigIntegerArithmeticPrecisionCap() {
+        // precision=3 caps at ~11 bits (formula: 3 * 10 / 3 + 1 = 11), so 
results > 2047 are rejected
+        final JexlArithmetic bounded = new JexlArithmetic(true, new 
MathContext(3), JexlArithmetic.BIGD_SCALE);
+        final JexlEngine jexl = new JexlBuilder().arithmetic(bounded).create();
+        // small values are fine
+        assertEquals(new BigInteger("3"), jexl.createScript("a + b", "a", "b")
+                .execute(null, BigInteger.ONE, BigInteger.valueOf(2L)));
+        // result > 2047 is rejected: 1500 + 1000 = 2500, bitLength=12 > 11
+        assertThrows(JexlException.class, () ->
+                jexl.createScript("a + b", "a", "b")
+                    .execute(null, BigInteger.valueOf(1500L), 
BigInteger.valueOf(1000L)));
+        // multiply pre-check: 64 (7 bits) * 64 (7 bits), sum of operand bits 
= 14 > 11
+        assertThrows(JexlException.class, () ->
+                jexl.createScript("a * b", "a", "b")
+                    .execute(null, BigInteger.valueOf(64L), 
BigInteger.valueOf(64L)));
+    }
+
+    /**
+     * f012: a regex pattern string longer than REGEX_PATTERN_MAX_LENGTH must 
throw.
+     */
+    @Test
+    void testRegexPatternTooLong() {
+        final JexlEngine jexl = new JexlBuilder().strict(true).create();
+        final JexlScript script = jexl.createScript("x =~ y", "x", "y");
+        final char[] chars = new char[JexlArithmetic.REGEX_PATTERN_MAX_LENGTH 
+ 1];
+        java.util.Arrays.fill(chars, 'a');
+        final String longPattern = new String(chars);
+        assertThrows(JexlException.class, () -> script.execute(null, "abc", 
longPattern));
+    }
+
+    /**
+     * f012: regex matching must respond to thread interruption so a 
catastrophic-backtracking
+     * pattern does not hang a cancellable engine indefinitely.
+     */
+    @Test
+    void testRegexMatchingInterruptible() throws InterruptedException {
+        final JexlEngine jexl = new JexlBuilder().cancellable(true).create();
+        final JexlScript script = jexl.createScript("x =~ y", "x", "y");
+        // Catastrophic backtracking pattern on non-matching input to force 
long character scanning
+        final String evilPattern = "(a+)+b";
+        // Use a larger set of 'a's to extend matching time
+        final char[] chars = new char[50];
+        java.util.Arrays.fill(chars, 'a');
+        final String evilValue = new String(chars) + "c";
+
+        final java.util.concurrent.atomic.AtomicReference<Exception> caught =
+            new java.util.concurrent.atomic.AtomicReference<>();
+        final java.util.concurrent.CountDownLatch started = new 
java.util.concurrent.CountDownLatch(1);
+
+        final Thread t = new Thread(() -> {
+            try {
+                started.countDown();
+                script.execute(null, evilValue, evilPattern);
+            } catch (final Exception e) {
+                caught.set(e);
+            }
+        });
+
+        t.start();
+        // Wait for thread to actually start executing
+        started.await();
+        // Give regex matching time to engage (50 'a's with (a+)+b pattern 
causes backtracking)
+        Thread.sleep(300);
+        // Interrupt the matching thread
+        t.interrupt();
+        // Wait for thread to complete (should exit promptly if 
InterruptibleCharSequence is working)
+        t.join(5000);
+
+        assertFalse(t.isAlive(), "Thread should have completed after 
interruption (regex should be interruptible)");
+        // The thread may complete without exception if the regex finishes 
faster than interruption catches it,
+        // or it may throw Cancel if interrupted during charset access. Both 
are acceptable here.
+        if (caught.get() != null) {
+            assertTrue(caught.get() instanceof JexlException.Cancel,
+                "If interrupted during matching, expected 
JexlException.Cancel, got " + caught.get().getClass().getSimpleName());
+        }

Review Comment:
   This test is timing-dependent and can pass even if interruptibility is 
broken (e.g., if the regex finishes before the interrupt, `caught` remains null 
and the test still succeeds). To make this deterministic, prefer asserting that 
an interrupt during matching reliably triggers `JexlException.Cancel` (or at 
least that an interrupt results in a failure outcome), and avoid fixed sleeps 
by using a stronger synchronization signal (e.g., larger input + polling until 
the worker thread is inside execution, or repeat until you observe the 
cancellation path within a bounded overall timeout).



##########
src/test/java/org/apache/commons/jexl3/ArithmeticTest.java:
##########
@@ -2409,4 +2409,101 @@ void setOptions(final JexlOptions options) {
         assertEquals("zero", jexl.createExpression("array.0").evaluate(jc));
         assertEquals("one", jexl.createExpression("array.1").evaluate(jc));
     }
+
+    // ----- security fixes f012 / f013 / f014 -----
+
+    /**
+     * f014: BigInteger literal with more digits than MAX_BIGINTEGER_DIGITS 
must be rejected at parse time.
+     */
+    @Test
+    void testBigIntegerLiteralTooLong() {
+        // normal H-literal still works
+        assertNotNull(JEXL.createScript("42H"));
+        // a literal just over the cap (256 + 1 digits + 'H') must fail to 
parse
+        final char[] digits = new char[256 + 1];

Review Comment:
   The test hard-codes the digit cap as 256, which will become brittle if the 
cap is changed (or if parsing behavior becomes engine-precision-dependent). 
Consider deriving this from a single source of truth (e.g., a constant that 
tests can access, or constructing an engine/thread-engine context with known 
precision and computing the expected max digit count from that) so the test 
remains stable across future adjustments.



##########
src/test/java/org/apache/commons/jexl3/ArithmeticTest.java:
##########
@@ -2409,4 +2409,101 @@ void setOptions(final JexlOptions options) {
         assertEquals("zero", jexl.createExpression("array.0").evaluate(jc));
         assertEquals("one", jexl.createExpression("array.1").evaluate(jc));
     }
+
+    // ----- security fixes f012 / f013 / f014 -----
+
+    /**
+     * f014: BigInteger literal with more digits than MAX_BIGINTEGER_DIGITS 
must be rejected at parse time.
+     */
+    @Test
+    void testBigIntegerLiteralTooLong() {
+        // normal H-literal still works
+        assertNotNull(JEXL.createScript("42H"));
+        // a literal just over the cap (256 + 1 digits + 'H') must fail to 
parse
+        final char[] digits = new char[256 + 1];
+        java.util.Arrays.fill(digits, '1');
+        final String huge = new String(digits) + "H";
+        assertThrows(JexlException.Parsing.class, () -> 
JEXL.createScript(huge));
+    }
+
+    /**
+     * f013: BigInteger arithmetic results that exceed the MathContext 
precision must throw.
+     */
+    @Test
+    void testBigIntegerArithmeticPrecisionCap() {
+        // precision=3 caps at ~11 bits (formula: 3 * 10 / 3 + 1 = 11), so 
results > 2047 are rejected
+        final JexlArithmetic bounded = new JexlArithmetic(true, new 
MathContext(3), JexlArithmetic.BIGD_SCALE);
+        final JexlEngine jexl = new JexlBuilder().arithmetic(bounded).create();
+        // small values are fine
+        assertEquals(new BigInteger("3"), jexl.createScript("a + b", "a", "b")
+                .execute(null, BigInteger.ONE, BigInteger.valueOf(2L)));
+        // result > 2047 is rejected: 1500 + 1000 = 2500, bitLength=12 > 11
+        assertThrows(JexlException.class, () ->
+                jexl.createScript("a + b", "a", "b")
+                    .execute(null, BigInteger.valueOf(1500L), 
BigInteger.valueOf(1000L)));
+        // multiply pre-check: 64 (7 bits) * 64 (7 bits), sum of operand bits 
= 14 > 11
+        assertThrows(JexlException.class, () ->
+                jexl.createScript("a * b", "a", "b")
+                    .execute(null, BigInteger.valueOf(64L), 
BigInteger.valueOf(64L)));
+    }
+
+    /**
+     * f012: a regex pattern string longer than REGEX_PATTERN_MAX_LENGTH must 
throw.
+     */
+    @Test
+    void testRegexPatternTooLong() {
+        final JexlEngine jexl = new JexlBuilder().strict(true).create();
+        final JexlScript script = jexl.createScript("x =~ y", "x", "y");
+        final char[] chars = new char[JexlArithmetic.REGEX_PATTERN_MAX_LENGTH 
+ 1];
+        java.util.Arrays.fill(chars, 'a');
+        final String longPattern = new String(chars);
+        assertThrows(JexlException.class, () -> script.execute(null, "abc", 
longPattern));
+    }
+
+    /**
+     * f012: regex matching must respond to thread interruption so a 
catastrophic-backtracking
+     * pattern does not hang a cancellable engine indefinitely.
+     */
+    @Test
+    void testRegexMatchingInterruptible() throws InterruptedException {
+        final JexlEngine jexl = new JexlBuilder().cancellable(true).create();
+        final JexlScript script = jexl.createScript("x =~ y", "x", "y");
+        // Catastrophic backtracking pattern on non-matching input to force 
long character scanning
+        final String evilPattern = "(a+)+b";
+        // Use a larger set of 'a's to extend matching time
+        final char[] chars = new char[50];
+        java.util.Arrays.fill(chars, 'a');
+        final String evilValue = new String(chars) + "c";
+
+        final java.util.concurrent.atomic.AtomicReference<Exception> caught =
+            new java.util.concurrent.atomic.AtomicReference<>();
+        final java.util.concurrent.CountDownLatch started = new 
java.util.concurrent.CountDownLatch(1);
+
+        final Thread t = new Thread(() -> {
+            try {
+                started.countDown();
+                script.execute(null, evilValue, evilPattern);
+            } catch (final Exception e) {
+                caught.set(e);
+            }
+        });
+
+        t.start();
+        // Wait for thread to actually start executing
+        started.await();
+        // Give regex matching time to engage (50 'a's with (a+)+b pattern 
causes backtracking)
+        Thread.sleep(300);
+        // Interrupt the matching thread
+        t.interrupt();
+        // Wait for thread to complete (should exit promptly if 
InterruptibleCharSequence is working)
+        t.join(5000);
+
+        assertFalse(t.isAlive(), "Thread should have completed after 
interruption (regex should be interruptible)");
+        // The thread may complete without exception if the regex finishes 
faster than interruption catches it,
+        // or it may throw Cancel if interrupted during charset access. Both 
are acceptable here.
+        if (caught.get() != null) {
+            assertTrue(caught.get() instanceof JexlException.Cancel,
+                "If interrupted during matching, expected 
JexlException.Cancel, got " + caught.get().getClass().getSimpleName());
+        }

Review Comment:
   This test is timing-dependent and can pass even if interruptibility is 
broken (e.g., if the regex finishes before the interrupt, `caught` remains null 
and the test still succeeds). To make this deterministic, prefer asserting that 
an interrupt during matching reliably triggers `JexlException.Cancel` (or at 
least that an interrupt results in a failure outcome), and avoid fixed sleeps 
by using a stronger synchronization signal (e.g., larger input + polling until 
the worker thread is inside execution, or repeat until you observe the 
cancellation path within a bounded overall timeout).



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to