diff --git a/HISTORY.rst b/HISTORY.rst index 8d636de3a..3a549e1ac 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -11,6 +11,8 @@ XXXX-XX-XX **Bug fixes** +- 2418_, [Linux]: fix race condition in case /proc/PID/stat does not exist, but + /proc/PID does, resulting in FileNotFoundError. - 2470_, [Linux]: `users()`_ may return "localhost" instead of the actual IP address of the user logged in. diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index d7e76c4f5..e1e9bb076 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -1722,7 +1722,12 @@ def wrapper(self, *args, **kwargs): raise NoSuchProcess(self.pid, self._name) except FileNotFoundError: self._raise_if_zombie() - if not os.path.exists("%s/%s" % (self._procfs_path, self.pid)): + # /proc/PID directory may still exist, but the files within + # it may not, indicating the process is gone, see: + # https://github.com/giampaolo/psutil/issues/2418 + if not os.path.exists( + "%s/%s/stat" % (self._procfs_path, self.pid) + ): raise NoSuchProcess(self.pid, self._name) raise diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py index dcfd60fbe..03c03cf14 100755 --- a/psutil/tests/test_linux.py +++ b/psutil/tests/test_linux.py @@ -2133,6 +2133,15 @@ def test_issue_1014(self): p.memory_maps() assert m.called + def test_issue_2418(self): + p = psutil.Process() + with mock_open_exception( + '/proc/%s/statm' % os.getpid(), FileNotFoundError + ): + with mock.patch("os.path.exists", return_value=False): + with pytest.raises(psutil.NoSuchProcess): + p.memory_info() + @pytest.mark.skipif(not HAS_RLIMIT, reason="not supported") def test_rlimit_zombie(self): # Emulate a case where rlimit() raises ENOSYS, which may