summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Boehm <hboehm@google.com>2019-03-03 18:32:41 -0800
committerHans Boehm <hboehm@google.com>2019-03-06 10:36:42 -0800
commit3dfe34c7e0295f733449eb150745fbfdfecffb5b (patch)
tree01303165fc333abb84fdede63973619709227d93
parentb80dbf3b7453f3bad1b8492268884dfbbded7519 (diff)
downloadcrcalc-3dfe34c7e0295f733449eb150745fbfdfecffb5b.tar.gz
It used to be the case that we could be interrupted or run out of memory after pushing nulls onto the two arrays capturing past computations, but before changing the nulls to real values. This left us in a corrupted state. Bug: 117705512 Test: crcalc tests, Calculator tests Change-Id: I35a5cf97967ede6fa298c178b5056461906bea1e
-rw-r--r--src/com/hp/creals/CR.java36
1 files changed, 25 insertions, 11 deletions
diff --git a/src/com/hp/creals/CR.java b/src/com/hp/creals/CR.java
index 35ab0dc..c5a1c41 100644
--- a/src/com/hp/creals/CR.java
+++ b/src/com/hp/creals/CR.java
@@ -114,6 +114,8 @@
// Fix a couple of unused variable bugs. Notably selector_sign was
// accidentally locally redeclared. (This turns out to be safe but useless.)
// hboehm@google.com 11/20/2018.
+// Fix an exception-safety issue in gl_pi_CR.approximate.
+// hboehm@google.com 3/3/2019.
package com.hp.creals;
@@ -1577,6 +1579,11 @@ class gl_pi_CR extends slow_CR {
private static CR SQRT_HALF = new sqrt_CR(ONE.shiftRight(1));
protected BigInteger approximate(int p) {
+ // Get us back into a consistent state if the last computation
+ // was interrupted after pushing onto b_prec.
+ if (b_prec.size() > b_val.size()) {
+ b_prec.remove(b_prec.size() - 1);
+ }
// Rough approximations are easy.
if (p >= 0) return scale(BigInteger.valueOf(3), -p);
// We need roughly log2(p) iterations. Each iteration should
@@ -1595,28 +1602,35 @@ class gl_pi_CR extends slow_CR {
// Current values correspond to n, next_ values to n + 1
// b_prec.size() == b_val.size() >= n + 1
final BigInteger next_a = a.add(b).shiftRight(1);
+ final BigInteger next_b;
final BigInteger a_diff = a.subtract(next_a);
- CR next_b_as_CR;
final BigInteger b_prod = a.multiply(b).shiftRight(-eval_prec);
- // We the compute square root approximations using a nested
+ // We compute square root approximations using a nested
// temporary CR computation, to avoid implementing BigInteger
// square roots separately.
final CR b_prod_as_CR = CR.valueOf(b_prod).shiftRight(-eval_prec);
if (b_prec.size() == n + 1) {
- // Need an n+1st slot.
- b_prec.add(null);
- b_val.add(null);
- next_b_as_CR = b_prod_as_CR.sqrt();
+ // Add an n+1st slot.
+ // Take care to make this exception-safe; b_prec and b_val
+ // must remain consistent, even if we are interrupted, or run
+ // out of memory. It's OK to just push on b_prec in that case.
+ final CR next_b_as_CR = b_prod_as_CR.sqrt();
+ next_b = next_b_as_CR.get_appr(eval_prec);
+ final BigInteger scaled_next_b = scale(next_b, -extra_eval_prec);
+ b_prec.add(p);
+ b_val.add(scaled_next_b);
} else {
// Reuse previous approximation to reduce sqrt iterations,
// hopefully to one.
- next_b_as_CR = new sqrt_CR(b_prod_as_CR, b_prec.get(n + 1),
- b_val.get(n + 1));
+ final CR next_b_as_CR =
+ new sqrt_CR(b_prod_as_CR,
+ b_prec.get(n + 1), b_val.get(n + 1));
+ next_b = next_b_as_CR.get_appr(eval_prec);
+ // We assume that set() doesn't throw for any reason.
+ b_prec.set(n + 1, p);
+ b_val.set(n + 1, scale(next_b, -extra_eval_prec));
}
// b_prec.size() == b_val.size() >= n + 2
- final BigInteger next_b = next_b_as_CR.get_appr(eval_prec);
- b_prec.set(n + 1, Integer.valueOf(p));
- b_val.set(n + 1, scale(next_b, -extra_eval_prec));
final BigInteger next_t =
t.subtract(a_diff.multiply(a_diff)
.shiftLeft(n + eval_prec)); // shift dist. usually neg.