-
-
Notifications
You must be signed in to change notification settings - Fork 30.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
TemporaryDirectory.__exit__
sometimes raises bad PermissionError
#113009
Comments
TemporaryDirectory.__exit__
raises PermissionError if other process keeping handle to a file in the tmp directory has just finished its execution
TemporaryDirectory.__exit__
raises PermissionError if other process keeping handle to a file in the tmp directory has just finished its executionTemporaryDirectory.__exit__
sometimes raises bad PermissionError
I was also able to reproduce this on Windows 11 Pro (22H2) with 3.11.7 and 3.12.1. It reproduces reliably with the code snippet above. I also tested this on Linux (Debian) with 3.11, 3.12.0 and 3.12.1 and was unable to repro. |
I have spent some time debugging another issue in the same piece of code that revealed this bug and probably I have found the cause of this as well. So consider the following block of code: import multiprocessing
import os
import tempfile
import time
import psutil
def _open_func(file_path):
with open(file_path, "w"):
time.sleep(1000)
def test(i):
with tempfile.TemporaryDirectory(suffix="used_by_another_process") as dir_path:
file_path = os.path.join(dir_path, "file_being_used")
proc = multiprocessing.Process(target=_open_func, args=(file_path,))
proc.start()
while not os.path.exists(file_path):
time.sleep(0.1)
proc.terminate()
proc.join()
try:
process = psutil.Process(proc.pid)
print(f'{process=} {i=}')
assert False
except psutil.Error:
pass
if __name__ == "__main__":
for i in range(1000):
test(i) Running So the real problem here is probably in fact that invoking It looks like the regression was introduced by this change: a2074911ba Please note that it is not present in 3.12.0 but in 3.12.1 it is. Previously, the |
Do you mean that in terminate(), when TerminateProcess() raises PermissionError and GetExitCodeProcess() returns an exit code different than STILL_ACTIVE, the process is in fact still running? |
Please log the returncode. Example: def test():
with tempfile.TemporaryDirectory(suffix="used_by_another_process") as dir_path:
file_path = os.path.join(dir_path, "file_being_used")
proc = multiprocessing.Process(target=_open_func, args=(file_path,))
print(repr(proc))
proc.start()
while not os.path.exists(file_path):
time.sleep(0.1)
proc.terminate()
print(f"terminate: {proc._popen.returncode=}")
proc.join()
print(f"join: {proc._popen.returncode=}") |
No, I mean that we now set
Thus
Process.join finishes.
The output of the program with additional debug logs:
|
I reproduced the bug on Python 3.13 on Windows. When the bug occurred, TerminateProcess() succeeded.
|
Do you mean that it's possible that a process is still running even if GetExitCodeProcess() returns an exit code other than STILL_ACTIVE? |
I think that the code now does not call
If |
On Windows, Process.terminate() no longer sets the returncode attribute to always call WaitForSingleObject() in Process.wait(). Previously, sometimes the process was still running after TerminateProcess() even if GetExitCodeProcess() is not STILL_ACTIVE.
On Windows, Process.terminate() no longer sets the returncode attribute to always call WaitForSingleObject() in Process.wait(). Previously, sometimes the process was still running after TerminateProcess() even if GetExitCodeProcess() is not STILL_ACTIVE.
So the difference with my recent change is that WaitForSingleObject() is no longer called. Sometimes, the child process is no longer running, and TemporaryDirectory removal is fine. Sometimes, the child process is still running, and TemporaryDirectory removal fails. I wrote PR #113128 to restore the old behavior: always call WaitForSingleObject(), even if TerminateProcess() succeeded and GetExitCodeProcess() is not STILL_ACTIVE. As an Unix programmer, the Windows API always surprise me. I expected TerminateProcess() to... terminate the process, not to schedule an asynchonous termination. It reminds me a similar process in asyncio with asynchronous cancellation: https://vstinner.github.io/asyncio-proactor-cancellation-from-hell.html |
Thank you for the fix! It now works fine with the change from your PR. Do you think it will be included in 3.12.2? |
On Windows, Process.terminate() no longer sets the returncode attribute to always call WaitForSingleObject() in Process.wait(). Previously, sometimes the process was still running after TerminateProcess() even if GetExitCodeProcess() is not STILL_ACTIVE.
…ythonGH-113128) On Windows, Process.terminate() no longer sets the returncode attribute to always call WaitForSingleObject() in Process.wait(). Previously, sometimes the process was still running after TerminateProcess() even if GetExitCodeProcess() is not STILL_ACTIVE. (cherry picked from commit 4026ad5) Co-authored-by: Victor Stinner <vstinner@python.org>
…ythonGH-113128) On Windows, Process.terminate() no longer sets the returncode attribute to always call WaitForSingleObject() in Process.wait(). Previously, sometimes the process was still running after TerminateProcess() even if GetExitCodeProcess() is not STILL_ACTIVE. (cherry picked from commit 4026ad5) Co-authored-by: Victor Stinner <vstinner@python.org>
Thanks for the bug report, analysis and testing my fix. It should now be fixed. Oops, I was surprised by async Windows API, different than Unix way of doing things. Backports will follow. |
…H-113128) (#113178) gh-113009: Fix multiprocessing Process.terminate() on Windows (GH-113128) On Windows, Process.terminate() no longer sets the returncode attribute to always call WaitForSingleObject() in Process.wait(). Previously, sometimes the process was still running after TerminateProcess() even if GetExitCodeProcess() is not STILL_ACTIVE. (cherry picked from commit 4026ad5) Co-authored-by: Victor Stinner <vstinner@python.org>
…H-113128) (#113177) gh-113009: Fix multiprocessing Process.terminate() on Windows (GH-113128) On Windows, Process.terminate() no longer sets the returncode attribute to always call WaitForSingleObject() in Process.wait(). Previously, sometimes the process was still running after TerminateProcess() even if GetExitCodeProcess() is not STILL_ACTIVE. (cherry picked from commit 4026ad5) Co-authored-by: Victor Stinner <vstinner@python.org>
…ython#113128) On Windows, Process.terminate() no longer sets the returncode attribute to always call WaitForSingleObject() in Process.wait(). Previously, sometimes the process was still running after TerminateProcess() even if GetExitCodeProcess() is not STILL_ACTIVE.
…ython#113128) On Windows, Process.terminate() no longer sets the returncode attribute to always call WaitForSingleObject() in Process.wait(). Previously, sometimes the process was still running after TerminateProcess() even if GetExitCodeProcess() is not STILL_ACTIVE.
…ython#113128) On Windows, Process.terminate() no longer sets the returncode attribute to always call WaitForSingleObject() in Process.wait(). Previously, sometimes the process was still running after TerminateProcess() even if GetExitCodeProcess() is not STILL_ACTIVE.
Bug report
TemporaryDirectory.__exit__
usually raises PermissionError if another process keeping a handle to a file in the tmp directory has just finished its execution.Bug description:
Please consider the following block of code:
Despite the child process being terminated and joined, the
__exit__
method ofTemporaryDirectory
sometimes raisesPermissionError
:It does not reproduce in 100% but most executions fail. I can reproduce it only using Python 3.12.1. It does not happen to me on 3.12.0 or 3.11. It seems to be a regression in the last release.
With some small sleep after the
proc.join()
it stops reproducing so it looks like a kind of race condition.The Windows version I use is:
Edition: Windows 10 Enterprise
Version: 21H2
OS build: 19044.3693
CPython versions tested on:
3.12
Operating systems tested on:
Windows
Linked PRs
The text was updated successfully, but these errors were encountered: