Author: Armin Rigo <ar...@tunes.org> Branch: Changeset: r85748:47e0578e9553 Date: 2016-07-18 10:29 +0200 http://bitbucket.org/pypy/pypy/changeset/47e0578e9553/
Log: Bug in the overflow detection code in the timeout argument to various thread/threading primitives. The cast from float to 64-bit integer needs to be done carefully, because floats don't have 64 bits of precision. diff --git a/pypy/module/thread/os_lock.py b/pypy/module/thread/os_lock.py --- a/pypy/module/thread/os_lock.py +++ b/pypy/module/thread/os_lock.py @@ -9,12 +9,9 @@ from pypy.interpreter.gateway import interp2app, unwrap_spec from pypy.interpreter.typedef import TypeDef from pypy.interpreter.error import oefmt -from rpython.rlib.rarithmetic import r_longlong +from rpython.rlib.rarithmetic import r_longlong, ovfcheck_float_to_longlong -LONGLONG_MAX = r_longlong(2 ** (r_longlong.BITS-1) - 1) -TIMEOUT_MAX = LONGLONG_MAX - RPY_LOCK_FAILURE, RPY_LOCK_ACQUIRED, RPY_LOCK_INTR = range(3) def parse_acquire_args(space, blocking, timeout): @@ -29,10 +26,12 @@ elif timeout == -1.0: microseconds = -1 else: - timeout *= 1e6 - if timeout > float(TIMEOUT_MAX): + # 0.0 => 0.0, but otherwise tends to round up + timeout = timeout * 1e6 + 0.999 + try: + microseconds = ovfcheck_float_to_longlong(timeout) + except OverflowError: raise oefmt(space.w_OverflowError, "timeout value is too large") - microseconds = r_longlong(timeout) return microseconds @@ -45,7 +44,8 @@ # Run signal handlers if we were interrupted space.getexecutioncontext().checksignals() if microseconds >= 0: - microseconds = r_longlong(endtime - (time.time() * 1e6)) + microseconds = r_longlong((endtime - (time.time() * 1e6)) + + 0.999) # Check for negative values, since those mean block # forever if microseconds <= 0: diff --git a/pypy/module/thread/test/test_lock.py b/pypy/module/thread/test/test_lock.py --- a/pypy/module/thread/test/test_lock.py +++ b/pypy/module/thread/test/test_lock.py @@ -64,6 +64,25 @@ else: assert self.runappdirect, "missing lock._py3k_acquire()" + def test_py3k_acquire_timeout_overflow(self): + import thread + lock = thread.allocate_lock() + if not hasattr(lock, '_py3k_acquire'): + skip("missing lock._py3k_acquire()") + maxint = 2**63 - 1 + boundary = int(maxint * 1e-6) + for i in [-100000, -10000, -1000, -100, -10, -1, 0, + 1, 10, 100, 1000, 10000, 100000]: + timeout = (maxint + i) * 1e-6 + try: + lock._py3k_acquire(True, timeout=timeout) + except OverflowError: + got_ovf = True + else: + got_ovf = False + lock.release() + assert (i, got_ovf) == (i, int(timeout * 1e6 + 0.999) > maxint) + @py.test.mark.xfail(machine()=='s390x', reason='may fail under heavy load') def test_ping_pong(self): # The purpose of this test is that doing a large number of ping-pongs diff --git a/rpython/rlib/rarithmetic.py b/rpython/rlib/rarithmetic.py --- a/rpython/rlib/rarithmetic.py +++ b/rpython/rlib/rarithmetic.py @@ -14,6 +14,8 @@ integer operation did overflow ovfcheck_float_to_int convert to an integer or raise OverflowError +ovfcheck_float_to_longlong + convert to a longlong or raise OverflowError r_longlong like r_int but double word size r_ulonglong @@ -182,6 +184,18 @@ # int(float(i)) != i because of rounding issues. # These are the minimum and maximum float value that can # successfully be casted to an int. + +# The following values are not quite +/-sys.maxint. +# Note the "<= x <" here, as opposed to "< x <" above. +# This is justified by test_typed in translator/c/test. +def ovfcheck_float_to_longlong(x): + from rpython.rlib.rfloat import isnan + if isnan(x): + raise OverflowError + if -9223372036854776832.0 <= x < 9223372036854775296.0: + return r_longlong(x) + raise OverflowError + if sys.maxint == 2147483647: def ovfcheck_float_to_int(x): from rpython.rlib.rfloat import isnan @@ -191,16 +205,8 @@ return int(x) raise OverflowError else: - # The following values are not quite +/-sys.maxint. - # Note the "<= x <" here, as opposed to "< x <" above. - # This is justified by test_typed in translator/c/test. def ovfcheck_float_to_int(x): - from rpython.rlib.rfloat import isnan - if isnan(x): - raise OverflowError - if -9223372036854776832.0 <= x < 9223372036854775296.0: - return int(x) - raise OverflowError + return int(ovfcheck_float_to_longlong(x)) def compute_restype(self_type, other_type): if self_type is other_type: _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit