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();