Apply reduction limit during evaluation
Caps the amount of virtual CPU time that a single goal
can consume during all searches for solutions. Each
reduction is one predicate invocation or alternate tried.
Change-Id: If3d41c14b9dd5330afe30f5a9ba502e2582c2e83
diff --git a/src/compiler/Compiler.java b/src/compiler/Compiler.java
index 1912022..cfc1a78 100644
--- a/src/compiler/Compiler.java
+++ b/src/compiler/Compiler.java
@@ -101,6 +101,7 @@
pcl = new BufferingPrologControl();
pcl.setEnabled(EnumSet.allOf(Prolog.Feature.class), true);
pcl.setPrologClassLoader(new PrologClassLoader(Compiler.class.getClassLoader()));
+ pcl.setReductionLimit(Long.MAX_VALUE);
options = EnumSet.noneOf(Option.class);
enableDefaultOptions();
}
diff --git a/src/exceptions/ReductionLimitException.java b/src/exceptions/ReductionLimitException.java
new file mode 100644
index 0000000..80ba112
--- /dev/null
+++ b/src/exceptions/ReductionLimitException.java
@@ -0,0 +1,14 @@
+package com.googlecode.prolog_cafe.exceptions;
+
+/**
+ * Thrown if a goal exceeds the configured reduction limit.
+ *
+ * @see com.googlecode.prolog_cafe.lang.PrologControl#setReductionLimit(long)
+ */
+public class ReductionLimitException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ public ReductionLimitException(long limit) {
+ super(String.format("exceeded reduction limit of %d", limit));
+ }
+}
diff --git a/src/lang/PrologControl.java b/src/lang/PrologControl.java
index 5882060..02bd6ae 100644
--- a/src/lang/PrologControl.java
+++ b/src/lang/PrologControl.java
@@ -2,6 +2,7 @@
import com.googlecode.prolog_cafe.exceptions.HaltException;
import com.googlecode.prolog_cafe.exceptions.PrologException;
+import com.googlecode.prolog_cafe.exceptions.ReductionLimitException;
import java.io.BufferedReader;
import java.io.BufferedWriter;
@@ -32,6 +33,10 @@
/** Holds a Prolog goal to be executed. */
protected Operation code;
+ /** How many operations can be executed before exceeding cost limit. */
+ private long reductionLimit = 1 << 20;
+ private long reductionsUsed;
+
/** Constructs a new <code>PrologControl</code>. */
public PrologControl() {
engine = new Prolog(this);
@@ -172,11 +177,14 @@
protected void executePredicate() throws PrologException {
Prolog engine = this.engine;
Operation code = this.code;
+ long reductionsRemaining = reductionLimit;
try {
engine.init();
do {
if (isEngineStopped()) return;
+ if (--reductionsRemaining <= 0)
+ throw new ReductionLimitException(reductionLimit);
code = code.exec(engine);
} while (engine.halt == 0);
@@ -184,11 +192,22 @@
throw new HaltException(engine.halt - 1);
}
} finally {
+ this.reductionsUsed = reductionLimit - reductionsRemaining;
this.code = code;
SymbolTerm.gc();
}
}
+ /** @return number of reductions used by execution. */
+ public long getReductions() {
+ return reductionsUsed;
+ }
+
+ /** Applies an upper limit on number of reductions. */
+ public void setReductionLimit(long limit) {
+ reductionLimit = Math.max(0, limit);
+ }
+
/** @param err stack trace to print (or log). */
public void printStackTrace(Throwable err) {
err.printStackTrace();