You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
gh-40556: Avoid mutating value field of constructed Integer
## Problem
Run the following code
```
a = [Integer(i) for i in range(100)]
try:
b = pow(2^100000, 3^100000, 5^100000)
finally:
del a
```
it will take a while. Press Ctrl-C before it finishes. Then **it will
hang** for a while before printing `KeyboardInterrupt`.
Meanwhile, the similar-looking code
```
a = [int(i) for i in range(100)]
try:
b = pow(2^100000, 3^100000, 5^100000)
finally:
del a
```
does not suffer from the problem.
## Reason
There are several places in SageMath code that does something like the
following
```
cdef Integer x = <Integer>PY_NEW(Integer)
sig_on()
mpz_powm(x.value, ...) # mutate x.value, slow
sig_off()
return x
```
If the user press ctrl-C, the destructor for `x` will be called, but
because `mpz_powm` is interrupted, `mpz_powm` may be in an
**inconsistent state**, so the destructor might do the wrong thing.
## The band-aid fix
The band-aid is described in
#24986 : a function
`sig_occurred()` is implemented in cysignals that checks whether a
signal thrown by `sig_on()` is currently being processed, and the
destructor of `Integer` checks:
```
if sig_occurred():
leak this Integer object just in case
else:
put this Integer object into a common object pool
```
## The issue
There are two problems with this.
* This leaks integers **indiscriminately**. In particular, in the
example above, **all** the 100 `Integer` objects in `a` are leaked, even
though only one is being mutated, being a local variable in the powering
method.
At any point in the code, we know exactly which object is currently
being mutated, as such this indiscriminate leak is unnecessary.
* `sig_occurred()` is **slow**: in the example above, it runs the
**garbage collector** each time it's called. So, the reason for the hang
is that after the user presses Ctrl-C, Python needs to run the garbage
collector 100 times before reporting `KeyboardInterrupt`.
## The actual fix
I look at the example reported in
#24986 — `coerce_actions.pyx`.
The problematic doctest is
```
E = EllipticCurve(GF(5), [4,0])
P = E([2,1,1])
from sage.doctest.util import ensure_interruptible_after
with ensure_interruptible_after(0.001): 2^(10^8) * P
```
by some analysis, I determine that the responsible function is
`_pow_long`.
As such, this pull request makes sure to not call the destructor when
the user interrupts.
After this pull request, we can remove the `sig_occurred()` workaround
introduced in #24986 .
## Other reported issues
* #39224
*
sagemath/cysignals#215 (comment)
* sagemath/cysignals#227
### 📝 Checklist
<!-- Put an `x` in all the boxes that apply. -->
- [x] The title is concise and informative.
- [x] The description explains in detail what this PR is about.
- [x] I have linked a relevant issue or discussion.
- [ ] I have created tests covering the changes.
- [ ] I have updated the documentation and checked the documentation
preview.
### ⌛ Dependencies
<!-- List all open PRs that this PR logically depends on. For example,
-->
<!-- - #12345: short description why this is a dependency -->
<!-- - #34567: ... -->
URL: #40556
Reported by: user202729
Reviewer(s): Michael Orlitzky, Travis Scrimshaw, user202729
0 commit comments