diff options
author | Hans Boehm <hboehm@google.com> | 2019-03-03 18:32:41 -0800 |
---|---|---|
committer | Hans Boehm <hboehm@google.com> | 2019-03-06 10:36:42 -0800 |
commit | 3dfe34c7e0295f733449eb150745fbfdfecffb5b (patch) | |
tree | 01303165fc333abb84fdede63973619709227d93 | |
parent | b80dbf3b7453f3bad1b8492268884dfbbded7519 (diff) | |
download | crcalc-3dfe34c7e0295f733449eb150745fbfdfecffb5b.tar.gz |
Make gl_pi_cr exception-safeandroid-q-preview-1android-o-mr1-iot-release-1.0.10
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.java | 36 |
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. |