gh-152635: Raise MemoryError rather than fail assert() when the lock allocation fails in _interpchannels.create()#152642
Conversation
now raises MemoryError Previously, an allocation failure when creating the lock for a channel in _interpchannels would trigger an assert. Caused by `handle_channel_error` being passed an error code of -1 which is only allowed if an exception has been set. (in this case, no exception was set) `channelsmod_create` now forwards the error code from `channel_create` which `handle_channel_error` already handled. Because the only way to get a -7 error code (was `ERR_CHANNEL_MUTEX_INIT`) is via an allocation failure in `PyThread_allocate_lock`, I made `handle_channel_error` raise a `MemoryError` for this case rather than a `ChannelError`. I also renamed the constant from `ERR_CHANNEL_MUTEX_INIT` to `ERR_CHANNEL_ALLOC_LOCK` to make this clearer for future changes.
…e test method to avoid import issues, improve the syntax for handling the expected MemoryError
has been moved to immediately before the _channels.create() call to ensure we correctly target the allocation failure.
|
Sorry for the force push, I didn't realise that github would do that with no warning on a simple rebase! |
| cid = None | ||
| try: | ||
| with self.assertRaises(MemoryError): | ||
| _testcapi.set_nomemory(0, 1) |
There was a problem hiding this comment.
The required start/stop vary by platform, for example on Linux on an unpatched main I get:
$ ./python /tmp/repro.py
Traceback (most recent call last):
File "/tmp/repro.py", line 6, in <module>
cid = _channels.create()
^^^^^^^^^
MemoryError
I'm also against testing with a loop where we try various numbers.
There was a problem hiding this comment.
Aah thanks, yes that's annoying, I'd hoped that there would be enough consistency for this to just work.
I noticed that gh-151239 didn't include any tests, maybe for this reason, and that the reproducer in the linked gh-150213 didn't catch this issue, I assume only because they started with n=1.
Having a for-loop over ranges in the tests seems quite comprehensive but maybe overkill for what it is?
I'd appreciate any suggestions here!
There was a problem hiding this comment.
In general, we just don't test this due to the associated complexity.
|
Something went wrong when merging, but we're only bothering Terry (who can unsubscribe in the sidebar ;-) so I don't think we need to recreate. |
Yeah, sorry, I'm working on it, I'm not used to this exact workflow |
…ailues seems to be rarely done
sobolevn
left a comment
There was a problem hiding this comment.
Thanks!
To sum up my thoughts:
MemoryErrorshould be raised where the error happens- In this case we should return
-1with the error set - We should remove
ERR_CHANNEL_MUTEX_INIThandling, because it will also fail with no memory when trying to create a new exception (probably) - But, I think we should keep the constant, so it's can be reused later, otherwise we would have a gap in numbers. A comment should be enough :)
Just set MemoryError and return directly on alloc failure, rather than go via the handler Leave existing error constant as-is for completeness Co-authored-by: sobolevn <mail@sobolevn.me>
handle_channel_error
Tested locally with:
def test_lock_allocation_failure(self):
import _testcapi
for n in range(0, 100):
cid = None
try:
_testcapi.set_nomemory(n, n+1)
cid = _channels.create()
except MemoryError:
pass
finally:
_testcapi.remove_mem_hooks()
if cid is not None:
_channels.close(cid, force=True)
_channels.destroy(cid)
|
GH-152671 is a backport of this pull request to the 3.15 branch. |
|
GH-152672 is a backport of this pull request to the 3.14 branch. |
|
GH-152673 is a backport of this pull request to the 3.13 branch. |
|
@sobolevn - Thanks for your help guiding me! |
|
Happy to help! Thanks for finding this bug :) |
… `_interpchannels.create()` (GH-152642) (#152673) gh-152635: Raise MemoryError when the lock allocation fails in `_interpchannels.create()` (GH-152642) Previously, an allocation failure when creating the lock for a channel in `_interpchannels` would trigger an assert. Caused by `handle_channel_error` being passed an error code of -1 which is only allowed if an exception has been set. (in this case, no exception was set) `channelsmod_create` now forwards the error code from `channel_create` which `handle_channel_error` already handled. (cherry picked from commit b383aa6) Co-authored-by: Steve Stagg <stestagg@gmail.com> Co-authored-by: sobolevn <mail@sobolevn.me>
… `_interpchannels.create()` (GH-152642) (#152671) gh-152635: Raise MemoryError when the lock allocation fails in `_interpchannels.create()` (GH-152642) Previously, an allocation failure when creating the lock for a channel in `_interpchannels` would trigger an assert. Caused by `handle_channel_error` being passed an error code of -1 which is only allowed if an exception has been set. (in this case, no exception was set) `channelsmod_create` now forwards the error code from `channel_create` which `handle_channel_error` already handled. (cherry picked from commit b383aa6) Co-authored-by: Steve Stagg <stestagg@gmail.com> Co-authored-by: sobolevn <mail@sobolevn.me>
… `_interpchannels.create()` (GH-152642) (#152672) gh-152635: Raise MemoryError when the lock allocation fails in `_interpchannels.create()` (GH-152642) Previously, an allocation failure when creating the lock for a channel in `_interpchannels` would trigger an assert. Caused by `handle_channel_error` being passed an error code of -1 which is only allowed if an exception has been set. (in this case, no exception was set) `channelsmod_create` now forwards the error code from `channel_create` which `handle_channel_error` already handled. (cherry picked from commit b383aa6) Co-authored-by: Steve Stagg <stestagg@gmail.com> Co-authored-by: sobolevn <mail@sobolevn.me>
Previously, an allocation failure when creating the lock for a channel in
_interpchannelswould trigger an assert. Caused byhandle_channel_errorbeing passed an error code of -1 which is only allowed if an exception has been set. (in this case, no exception was set)channelsmod_createnow forwards the error code fromchannel_createwhichhandle_channel_erroralready handled.Because the only way to get a -7 error code (was
ERR_CHANNEL_MUTEX_INIT) is via an allocation failure inPyThread_allocate_lock, I madehandle_channel_errorraise aMemoryErrorfor this case rather than aChannelErrorwhich seems more consistent with general practice.I also renamed the constant from
ERR_CHANNEL_MUTEX_INITtoERR_CHANNEL_ALLOC_LOCKto make this clearer for future changes.There's a test to cover this, which does rely on
_interpchannels.createcode paths not changing too wildly (first allocation has to be the lock), but even if that changes, the test isn't invalid.Full test suite passes, except
test_structwhich is known in gh-142414 on MacOS and WindowsTest summary
_interpchannels.create()debug aborts if channel lock allocation fails #152635