Hello!
I’ve found something that started to break in 3.13, and I want to confirm that it is a bug (so I open an issue) and not something weirder (maybe in my code).
This is a simple script to expose the problem. It creates a portion of shared memory, then forks. The child writes in that shared memory. The parent waits for the child to finish, reads the shared memory, do the proper cleanup, and then shows the result.
import os
import struct
from multiprocessing import shared_memory
type_size = struct.calcsize('B')
shm = shared_memory.SharedMemory(create=True, size=type_size * 2)
pid = os.fork()
if pid == 0:
# child
values = [1, 2]
offset = 0
struct.pack_into("2B", shm.buf, offset, *values)
exit(0)
# only the parent will reach here
os.wait()
# get produced value and cleanup the shared memory
result = struct.unpack("2B", shm.buf)
shm.close()
shm.unlink()
print(result)
It works just fine in 3.12:
$ python3.12 badtrack.py
(1, 2)
But in 3.13 (and 3.14, and 3.15 just compiled from main):
$ python3.13 badtrack.py
Exception ignored in: <function ResourceTracker.__del__ at 0x784fa13d0540>
Traceback (most recent call last):
File "/usr/lib/python3.13/multiprocessing/resource_tracker.py", line 84, in __del__
File "/usr/lib/python3.13/multiprocessing/resource_tracker.py", line 93, in _stop
File "/usr/lib/python3.13/multiprocessing/resource_tracker.py", line 118, in _stop_locked
ChildProcessError: [Errno 10] No child processes
(1, 2)
Note that the script still ends “successfully”, but there is that ChildProcessError in the middle. The weird thing is that the error happens when closing the resource tracker, when it does waitpid(self._pid), being that _pid the process id of the tracker itself… it looks it finished before, so it’s gone at that moment.
This is 100% reproducible in my machine (Ubuntu 24.04, kernel 6.8.0-79-generic, x86_64 architecture).
Of course, calling SharedMemory with track=False makes the problem go away (but this workaround is not possible when using ShareableList).
Thanks!
. Facundo