Skip to content

Commit

Permalink
Implement ability to timeout execution of a regexp
Browse files Browse the repository at this point in the history
  • Loading branch information
enebo committed May 14, 2024
1 parent c13681d commit 619f7c4
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 1 deletion.
8 changes: 7 additions & 1 deletion src/org/joni/ByteCodeMachine.java
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ private final int execute(final boolean checkThreadInterrupt) throws Interrupted
int interruptCheckCounter = 0;
while (true) {
if (interruptCheckCounter++ >= interruptCheckEvery) {
if (timeout != -1) handleTimeout();
handleInterrupted(checkThreadInterrupt);
interruptCheckCounter = 0;
}
Expand Down Expand Up @@ -306,11 +307,16 @@ private final int execute(final boolean checkThreadInterrupt) throws Interrupted
} // main while
}

private void handleTimeout() throws InterruptedException {
if (System.nanoTime() - startTime > timeout) throw TIMEOUT_EXCEPTION;
}

private final int executeSb(final boolean checkThreadInterrupt) throws InterruptedException {
final int[] code = this.code;
int interruptCheckCounter = 0;
while (true) {
if (interruptCheckCounter++ >= interruptCheckEvery) {
if (timeout != -1) handleTimeout();
handleInterrupted(checkThreadInterrupt);
interruptCheckCounter = 0;
}
Expand Down Expand Up @@ -447,7 +453,7 @@ private final int executeSb(final boolean checkThreadInterrupt) throws Interrupt
private void handleInterrupted(final boolean checkThreadInterrupt) throws InterruptedException {
if (interrupted || (checkThreadInterrupt && Thread.currentThread().isInterrupted())) {
Thread.interrupted();
throw new InterruptedException();
throw INTERRUPTED_EXCEPTION;
}
interruptCheckEvery = Math.min(interruptCheckEvery << 1, MAX_INTERRUPT_CHECK_EVERY);
}
Expand Down
21 changes: 21 additions & 0 deletions src/org/joni/Matcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@
import org.jcodings.constants.CharacterType;
import org.jcodings.specific.ASCIIEncoding;
import org.joni.constants.internal.AnchorType;
import org.joni.exception.TimeoutException;

public abstract class Matcher extends IntHolder {
static final InterruptedException INTERRUPTED_EXCEPTION = new InterruptedException();
static final InterruptedException TIMEOUT_EXCEPTION = new TimeoutException();
public static final int FAILED = -1;
public static final int INTERRUPTED = -2;

Expand All @@ -49,13 +52,26 @@ public abstract class Matcher extends IntHolder {
protected int msaBegin;
protected int msaEnd;

protected long timeout; // nanoseconds

// nanoseconds since entering searchCommon (underlying machines will check during interrupt checks
// which will cheapen how often we look but also it should be granular enough to not matter).
protected long startTime;

Matcher(Regex regex, Region region, byte[]bytes, int p, int end) {
this(regex, region, bytes, p, end, -1);
}

// FIXME: For next major version this should be the main constructor and MatcherFactory should
// have the abstract method be for this.
Matcher(Regex regex, Region region, byte[]bytes, int p, int end, long timeout) {
this.regex = regex;
this.enc = regex.enc;
this.bytes = bytes;
this.str = p;
this.end = end;
this.msaRegion = region;
this.timeout = timeout;
}

// main matching method
Expand Down Expand Up @@ -323,6 +339,7 @@ public final int searchInterruptible(int gpos, int start, int range, int option)
}

private final int searchCommon(int gpos, int start, int range, int option, boolean interrupt) throws InterruptedException {
if (timeout != -1) startTime = System.nanoTime();
int s, prev;
int origStart = start;
int origRange = range;
Expand Down Expand Up @@ -640,4 +657,8 @@ static void debugSearch(String name, int textP, int textEnd, int textRange) {
Config.log.println(name + ": text: " + textP + ", text_end: " + textEnd + ", text_range: " + textRange);
}

public void setTimeout(long timeout) {
System.out.println("TIMEOUT = " + timeout);
this.timeout = timeout;
}
}
6 changes: 6 additions & 0 deletions src/org/joni/MatcherFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@
abstract class MatcherFactory {
abstract Matcher create(Regex regex, Region region, byte[]bytes, int p, int end);

public Matcher create(Regex regex, Region region, byte[]bytes, int p, int end, long timeout) {
Matcher matcher = create(regex, region, bytes, p, end);
matcher.setTimeout(timeout);
return matcher;
}

static final MatcherFactory DEFAULT = new MatcherFactory() {
@Override
Matcher create(Regex regex, Region region, byte[]bytes, int p, int end) {
Expand Down
8 changes: 8 additions & 0 deletions src/org/joni/Regex.java
Original file line number Diff line number Diff line change
Expand Up @@ -185,10 +185,18 @@ public Matcher matcher(byte[]bytes, int p, int end) {
return factory.create(this, numMem == 0 ? null : Region.newRegion(numMem + 1), bytes, p, end);
}

public Matcher matcher(byte[]bytes, int p, int end, long timeout) {
return factory.create(this, numMem == 0 ? null : Region.newRegion(numMem + 1), bytes, p, end, timeout);
}

public Matcher matcherNoRegion(byte[]bytes, int p, int end) {
return factory.create(this, null, bytes, p, end);
}

public Matcher matcherNoRegion(byte[]bytes, int p, int end, long timeout) {
return factory.create(this, null, bytes, p, end, timeout);
}

public int numberOfCaptures() {
return numMem;
}
Expand Down
7 changes: 7 additions & 0 deletions src/org/joni/exception/TimeoutException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.joni.exception;

public class TimeoutException extends InterruptedException {
public TimeoutException() {
super();
}
}

0 comments on commit 619f7c4

Please sign in to comment.