Uh oh!
There was an error while loading. Please reload this page.
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.
stestagg
commented
Jun 30, 2026
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.
StanFromIreland
commented
Jun 30, 2026
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. |
stestagg
commented
Jun 30, 2026
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 :)
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
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>
Uh oh!
There was an error while loading. Please reload this page.
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)
Uh oh!
There was an error while loading. Please reload this page.
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. |
stestagg
commented
Jun 30, 2026
@sobolevn - Thanks for your help guiding me! |
sobolevn
commented
Jun 30, 2026
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>
* main: (266 commits) pythongh-151626: Fix tests that fail when PYTHONPYCACHEPREFIX is set (pythonGH-151952) pythongh-152728: IDLE - move 3 toplevel fix_xyz functions to idlelb.util (python#152729) pythongh-152711: Add pythoninfo-build command to Platforms/Android (python#152713) pythongh-152715: Add pythoninfo-build command to Platforms/Apple (python#152716) pythongh-152433: Windows: enable mmapmodule for UWP (python#152473) pythongh-152433: Windows: use GetFileSizeEx instead of GetFileSize for memory mapped files (python#152383) pythonGH-81881: Raise `SpecialFileError` for sockets and devices in `shutil.copyfile` (python#142693) pythongh-152502: Detect the curses mouse interface and is_* methods portably (pythonGH-152705) pythongh-145857: Replace `DELETE_GLOBAL` with `PUSH_NULL; STORE_GLOBAL` (pythonGH-146314) pythongh-145854: Replace `DELETE_NAME` with `PUSH_NULL; STORE_NAME` (pythonGH-146006) pythongh-152680: Detect container/VM in test.pythoninfo (python#152668) pythongh-152682: Fix NULL dereference on OOM in `symtable_visit_type_param_bound_or_default` (python#152684) pythongh-151881: Skip tk_inactive negativity check on Windows (pythonGH-152683) pythongh-152546: Refactor `mappingproxy.__new__` to use `PyDictProxy_New` (python#152547) pythongh-151126: Fix a possible crash during the startup with no memory under `Py_STACKREF_DEBUG` (python#152478) pythongh-152635: Raise MemoryError when the lock allocation fails in `_interpchannels.create()` (python#152642) pythongh-151029: Fix `test_remote_exec_deleted_static_executable` on static installed builds (pythonGH-152653) pythongh-121249: Deprecate using F/D type codes in the struct module (python#152309) pythongh-152192: Fix JUMP_BACKWARD passing a truncated oparg to the jit tracer (pythonGH-152382) Don't require the `_test{internal}capi` modules in `test_monitoring.py` (python#152311) ...
| int64_t cid = channel_create(&_globals.channels, defaults); | ||
| if (cid < 0) { | ||
| (void)handle_channel_error(-1, self, cid); | ||
| (void)handle_channel_error(cid, self, cid); |
There was a problem hiding this comment.
It causes a warning when compiling CPython using MSVC on Windows x64.
e.g., in GitHub Actions:
[Build CPython: Modules/_interpchannelsmodule.c#L2938]
'function': conversion from 'int64_t' to 'int', possible loss of data [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]
There was a problem hiding this comment.
I will fix it, thanks for the report!
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