Skip to content

Commit

Permalink
process_iter(): use another global var to keep track of reused PIDs
Browse files Browse the repository at this point in the history
  • Loading branch information
giampaolo committed Jun 9, 2024
1 parent 9421bf8 commit 89b6096
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 5 deletions.
9 changes: 7 additions & 2 deletions psutil/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
from ._common import NoSuchProcess
from ._common import TimeoutExpired
from ._common import ZombieProcess
from ._common import debug
from ._common import memoize_when_activated
from ._common import wrap_numbers as _wrap_numbers
from ._compat import PY3 as _PY3
Expand Down Expand Up @@ -613,8 +614,7 @@ def is_running(self):
# time) and that is verified in __eq__.
self._pid_reused = self != Process(self.pid)
if self._pid_reused:
# remove this PID from `process_iter()` internal cache
_pmap.pop(self.pid, None)
_pids_reused.add(self.pid)
raise NoSuchProcess(self.pid)
return True
except ZombieProcess:
Expand Down Expand Up @@ -1464,6 +1464,7 @@ def pid_exists(pid):


_pmap = {}
_pids_reused = set()


def process_iter(attrs=None, ad_value=None):
Expand Down Expand Up @@ -1501,6 +1502,10 @@ def remove(pid):
gone_pids = b - a
for pid in gone_pids:
remove(pid)
while _pids_reused:
pid = _pids_reused.pop()
debug("refreshing Process instance for reused PID %s" % pid)
remove(pid)
try:
ls = sorted(list(pmap.items()) + list(dict.fromkeys(new_pids).items()))
for pid, proc in ls:
Expand Down
4 changes: 2 additions & 2 deletions psutil/tests/test_linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -1120,10 +1120,10 @@ def ifconfig(nic):
except RuntimeError:
continue
self.assertAlmostEqual(
stats.bytes_recv, ifconfig_ret['bytes_recv'], delta=1024 * 5
stats.bytes_recv, ifconfig_ret['bytes_recv'], delta=1024 * 10
)
self.assertAlmostEqual(
stats.bytes_sent, ifconfig_ret['bytes_sent'], delta=1024 * 5
stats.bytes_sent, ifconfig_ret['bytes_sent'], delta=1024 * 10
)
self.assertAlmostEqual(
stats.packets_recv, ifconfig_ret['packets_recv'], delta=1024
Expand Down
1 change: 1 addition & 0 deletions psutil/tests/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ def test__all__(self):
dir_psutil = dir(psutil)
for name in dir_psutil:
if name in (
'debug',
'long',
'tests',
'test',
Expand Down
14 changes: 13 additions & 1 deletion psutil/tests/test_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from psutil._compat import PY3
from psutil._compat import FileNotFoundError
from psutil._compat import long
from psutil._compat import redirect_stderr
from psutil._compat import super
from psutil.tests import APPVEYOR
from psutil.tests import CI_TESTING
Expand Down Expand Up @@ -763,7 +764,6 @@ def test_long_cmdline(self):
if ret == []:
# https://github.com/giampaolo/psutil/issues/2250
raise unittest.SkipTest("OPENBSD: returned EBUSY")
self.assertEqual(p.cmdline(), cmdline)

self.assertEqual(p.cmdline(), cmdline)

Expand Down Expand Up @@ -1378,15 +1378,27 @@ def test_zombie_process_status_w_exc(self):

def test_reused_pid(self):
# Emulate a case where PID has been reused by another process.
if PY3:
from io import StringIO
else:
from StringIO import StringIO

subp = self.spawn_testproc()
p = psutil.Process(subp.pid)
p._ident = (p.pid, p.create_time() + 100)

list(psutil.process_iter())
self.assertIn(p.pid, psutil._pmap)
assert not p.is_running()

# make sure is_running() removed PID from process_iter()
# internal cache
with redirect_stderr(StringIO()) as f:
list(psutil.process_iter())
self.assertIn(
"refreshing Process instance for reused PID %s" % p.pid,
f.getvalue(),
)
self.assertNotIn(p.pid, psutil._pmap)

assert p != psutil.Process(subp.pid)
Expand Down

0 comments on commit 89b6096

Please sign in to comment.