summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog11
-rw-r--r--bignum.c30
-rw-r--r--test/ruby/test_bignum.rb30
3 files changed, 56 insertions, 15 deletions
diff --git a/ChangeLog b/ChangeLog
index 22fc668767..284aa3be68 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+Tue Nov 27 21:29:00 2012 Kenta Murata <mrkn@mrkn.jp>
+
+ * bignum.c (bigdivrem): optimize the way to retry calculation of
+ bigdivrem so that the calculation is started from the point where
+ the last interruption was occurred.
+
+ * bignum.c (bigdivrem1): ditto.
+
+ * test/ruby/test_bignum.rb: add a test case for rb_bigdivrem in the
+ case that an interruption is occurred during bigdivrem1 is running.
+
Tue Nov 27 19:56:43 2012 Koichi Sasada <ko1@atdot.net>
* vm.c (rb_vm_make_env_object): make Proc object if Env is possible
diff --git a/bignum.c b/bignum.c
index 798571deba..666305756a 100644
--- a/bignum.c
+++ b/bignum.c
@@ -2655,7 +2655,7 @@ rb_big_mul(VALUE x, VALUE y)
}
struct big_div_struct {
- long nx, ny;
+ long nx, ny, j, nyzero;
BDIGIT *yds, *zds;
volatile VALUE stop;
};
@@ -2664,21 +2664,23 @@ static void *
bigdivrem1(void *ptr)
{
struct big_div_struct *bds = (struct big_div_struct*)ptr;
- long nx = bds->nx, ny = bds->ny;
- long i, j, nyzero;
+ long ny = bds->ny;
+ long i, j;
BDIGIT *yds = bds->yds, *zds = bds->zds;
BDIGIT_DBL t2;
BDIGIT_DBL_SIGNED num;
BDIGIT q;
- j = nx==ny?nx+1:nx;
- for (nyzero = 0; !yds[nyzero]; nyzero++);
+ j = bds->j;
do {
- if (bds->stop) return 0;
+ if (bds->stop) {
+ bds->j = j;
+ return 0;
+ }
if (zds[j] == yds[ny-1]) q = (BDIGIT)BIGRAD-1;
else q = (BDIGIT)((BIGUP(zds[j]) + zds[j-1])/yds[ny-1]);
if (q) {
- i = nyzero; num = 0; t2 = 0;
+ i = bds->nyzero; num = 0; t2 = 0;
do { /* multiply and subtract */
BDIGIT_DBL ee;
t2 += (BDIGIT_DBL)yds[i] * q;
@@ -2713,22 +2715,16 @@ rb_big_stop(void *ptr)
}
static VALUE
-bigdivrem(VALUE x, VALUE y_, volatile VALUE *divp, volatile VALUE *modp)
+bigdivrem(VALUE x, VALUE y, volatile VALUE *divp, volatile VALUE *modp)
{
- VALUE y;
struct big_div_struct bds;
- long nx, ny;
+ long nx = RBIGNUM_LEN(x), ny = RBIGNUM_LEN(y);
long i, j;
VALUE z, yy, zz;
BDIGIT *xds, *yds, *zds, *tds;
BDIGIT_DBL t2;
BDIGIT dd, q;
- retry:
- y = y_;
- nx = RBIGNUM_LEN(x);
- ny = RBIGNUM_LEN(y);
-
if (BIGZEROP(y)) rb_num_zerodiv();
xds = BDIGITS(x);
yds = BDIGITS(y);
@@ -2799,7 +2795,11 @@ bigdivrem(VALUE x, VALUE y_, volatile VALUE *divp, volatile VALUE *modp)
bds.zds = zds;
bds.yds = yds;
bds.stop = Qfalse;
+ bds.j = nx==ny?nx+1:nx;
+ for (bds.nyzero = 0; !yds[bds.nyzero]; bds.nyzero++);
if (nx > 10000 || ny > 10000) {
+ retry:
+ bds.stop = Qfalse;
rb_thread_call_without_gvl(bigdivrem1, &bds, rb_big_stop, &bds);
if (bds.stop == Qtrue) {
diff --git a/test/ruby/test_bignum.rb b/test/ruby/test_bignum.rb
index b1497a9146..1ff23e2a00 100644
--- a/test/ruby/test_bignum.rb
+++ b/test/ruby/test_bignum.rb
@@ -581,6 +581,36 @@ class TestBignum < Test::Unit::TestCase
assert_interrupt {(65536 ** 65536).to_s}
end
+ def test_interrupt_during_bigdivrem
+ return unless Process.respond_to?(:kill)
+ begin
+ trace = []
+ oldtrap = Signal.trap(:INT) {|sig| trace << :int }
+ a = 456 ** 100
+ b = 123 ** 100
+ c = nil
+ 100.times do |n|
+ a **= 3
+ b **= 3
+ trace.clear
+ th = Thread.new do
+ sleep 0.1; Process.kill :INT, $$
+ sleep 0.1; Process.kill :INT, $$
+ end
+ c = a / b
+ trace << :end
+ th.join
+ if trace == [:int, :int, :end]
+ assert_equal(a / b, c)
+ return
+ end
+ end
+ skip "cannot create suitable test case"
+ ensure
+ Signal.trap(:INT, oldtrap) if oldtrap
+ end
+ end
+
def test_too_big_to_s
if (big = 2**31-1).is_a?(Fixnum)
return