Skip to content

Commit

Permalink
#1373: different approach to oneshot() cache (pass Process instances …
Browse files Browse the repository at this point in the history
…around - which is faster)
  • Loading branch information
giampaolo committed Dec 13, 2018
1 parent 8351fa4 commit 2cdf81d
Show file tree
Hide file tree
Showing 10 changed files with 70 additions and 54 deletions.
16 changes: 8 additions & 8 deletions psutil/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,23 +471,23 @@ def oneshot(self):
self._oneshot_inctx = True
try:
# cached in case cpu_percent() is used
self.cpu_times.cache_activate()
self.cpu_times.cache_activate(self)
# cached in case memory_percent() is used
self.memory_info.cache_activate()
self.memory_info.cache_activate(self)
# cached in case parent() is used
self.ppid.cache_activate()
self.ppid.cache_activate(self)
# cached in case username() is used
if POSIX:
self.uids.cache_activate()
self.uids.cache_activate(self)
# specific implementation cache
self._proc.oneshot_enter()
yield
finally:
self.cpu_times.cache_deactivate()
self.memory_info.cache_deactivate()
self.ppid.cache_deactivate()
self.cpu_times.cache_deactivate(self)
self.memory_info.cache_deactivate(self)
self.ppid.cache_deactivate(self)
if POSIX:
self.uids.cache_deactivate()
self.uids.cache_deactivate(self)
self._proc.oneshot_exit()
self._oneshot_inctx = False

Expand Down
25 changes: 13 additions & 12 deletions psutil/_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ def memoize_when_activated(fun):
1
>>>
>>> # activated
>>> foo.cache_activate()
>>> foo.cache_activate(self)
>>> foo()
1
>>> foo()
Expand All @@ -336,26 +336,27 @@ def memoize_when_activated(fun):
"""
@functools.wraps(fun)
def wrapper(self):
if not wrapper.cache_activated:
if not hasattr(self, "_cache"):
return fun(self)
else:
try:
ret = cache[fun]
ret = self._cache[fun]
except KeyError:
ret = cache[fun] = fun(self)
ret = self._cache[fun] = fun(self)
return ret

def cache_activate():
"""Activate cache."""
wrapper.cache_activated = True
def cache_activate(proc):
"""Activate cache. Expects a Process instance. Cache will be
stored as a "_cache" instance attribute."""
proc._cache = {}

def cache_deactivate():
def cache_deactivate(proc):
"""Deactivate and clear cache."""
wrapper.cache_activated = False
cache.clear()
try:
del proc._cache
except AttributeError:
pass

cache = {}
wrapper.cache_activated = False
wrapper.cache_activate = cache_activate
wrapper.cache_deactivate = cache_deactivate
return wrapper
Expand Down
14 changes: 7 additions & 7 deletions psutil/_psaix.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ def wrapper(self, *args, **kwargs):
class Process(object):
"""Wrapper class around underlying C implementation."""

__slots__ = ["pid", "_name", "_ppid", "_procfs_path"]
__slots__ = ["pid", "_name", "_ppid", "_procfs_path", "_cache"]

def __init__(self, pid):
self.pid = pid
Expand All @@ -363,14 +363,14 @@ def __init__(self, pid):
self._procfs_path = get_procfs_path()

def oneshot_enter(self):
self._proc_name_and_args.cache_activate()
self._proc_basic_info.cache_activate()
self._proc_cred.cache_activate()
self._proc_name_and_args.cache_activate(self)
self._proc_basic_info.cache_activate(self)
self._proc_cred.cache_activate(self)

def oneshot_exit(self):
self._proc_name_and_args.cache_deactivate()
self._proc_basic_info.cache_deactivate()
self._proc_cred.cache_deactivate()
self._proc_name_and_args.cache_deactivate(self)
self._proc_basic_info.cache_deactivate(self)
self._proc_cred.cache_deactivate(self)

@memoize_when_activated
def _proc_name_and_args(self):
Expand Down
6 changes: 3 additions & 3 deletions psutil/_psbsd.py
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ def wrap_exceptions_procfs(inst):
class Process(object):
"""Wrapper class around underlying C implementation."""

__slots__ = ["pid", "_name", "_ppid"]
__slots__ = ["pid", "_name", "_ppid", "_cache"]

def __init__(self, pid):
self.pid = pid
Expand All @@ -609,10 +609,10 @@ def oneshot(self):
return ret

def oneshot_enter(self):
self.oneshot.cache_activate()
self.oneshot.cache_activate(self)

def oneshot_exit(self):
self.oneshot.cache_deactivate()
self.oneshot.cache_deactivate(self)

@wrap_exceptions
def name(self):
Expand Down
14 changes: 7 additions & 7 deletions psutil/_pslinux.py
Original file line number Diff line number Diff line change
Expand Up @@ -1528,7 +1528,7 @@ def wrapper(self, *args, **kwargs):
class Process(object):
"""Linux process implementation."""

__slots__ = ["pid", "_name", "_ppid", "_procfs_path"]
__slots__ = ["pid", "_name", "_ppid", "_procfs_path", "_cache"]

def __init__(self, pid):
self.pid = pid
Expand Down Expand Up @@ -1585,14 +1585,14 @@ def _read_smaps_file(self):
return f.read().strip()

def oneshot_enter(self):
self._parse_stat_file.cache_activate()
self._read_status_file.cache_activate()
self._read_smaps_file.cache_activate()
self._parse_stat_file.cache_activate(self)
self._read_status_file.cache_activate(self)
self._read_smaps_file.cache_activate(self)

def oneshot_exit(self):
self._parse_stat_file.cache_deactivate()
self._read_status_file.cache_deactivate()
self._read_smaps_file.cache_deactivate()
self._parse_stat_file.cache_deactivate(self)
self._read_status_file.cache_deactivate(self)
self._read_smaps_file.cache_deactivate(self)

@wrap_exceptions
def name(self):
Expand Down
10 changes: 5 additions & 5 deletions psutil/_psosx.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ def catch_zombie(proc):
class Process(object):
"""Wrapper class around underlying C implementation."""

__slots__ = ["pid", "_name", "_ppid"]
__slots__ = ["pid", "_name", "_ppid", "_cache"]

def __init__(self, pid):
self.pid = pid
Expand All @@ -403,12 +403,12 @@ def _get_pidtaskinfo(self):
return ret

def oneshot_enter(self):
self._get_kinfo_proc.cache_activate()
self._get_pidtaskinfo.cache_activate()
self._get_kinfo_proc.cache_activate(self)
self._get_pidtaskinfo.cache_activate(self)

def oneshot_exit(self):
self._get_kinfo_proc.cache_deactivate()
self._get_pidtaskinfo.cache_deactivate()
self._get_kinfo_proc.cache_deactivate(self)
self._get_pidtaskinfo.cache_deactivate(self)

@wrap_exceptions
def name(self):
Expand Down
14 changes: 7 additions & 7 deletions psutil/_pssunos.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ def wrapper(self, *args, **kwargs):
class Process(object):
"""Wrapper class around underlying C implementation."""

__slots__ = ["pid", "_name", "_ppid", "_procfs_path"]
__slots__ = ["pid", "_name", "_ppid", "_procfs_path", "_cache"]

def __init__(self, pid):
self.pid = pid
Expand All @@ -377,14 +377,14 @@ def __init__(self, pid):
self._procfs_path = get_procfs_path()

def oneshot_enter(self):
self._proc_name_and_args.cache_activate()
self._proc_basic_info.cache_activate()
self._proc_cred.cache_activate()
self._proc_name_and_args.cache_activate(self)
self._proc_basic_info.cache_activate(self)
self._proc_cred.cache_activate(self)

def oneshot_exit(self):
self._proc_name_and_args.cache_deactivate()
self._proc_basic_info.cache_deactivate()
self._proc_cred.cache_deactivate()
self._proc_name_and_args.cache_deactivate(self)
self._proc_basic_info.cache_deactivate(self)
self._proc_cred.cache_deactivate(self)

@memoize_when_activated
def _proc_name_and_args(self):
Expand Down
6 changes: 3 additions & 3 deletions psutil/_pswindows.py
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,7 @@ def wrapper(self, *args, **kwargs):
class Process(object):
"""Wrapper class around underlying C implementation."""

__slots__ = ["pid", "_name", "_ppid"]
__slots__ = ["pid", "_name", "_ppid", "_cache"]

def __init__(self, pid):
self.pid = pid
Expand All @@ -655,10 +655,10 @@ def __init__(self, pid):
# --- oneshot() stuff

def oneshot_enter(self):
self.oneshot_info.cache_activate()
self.oneshot_info.cache_activate(self)

def oneshot_exit(self):
self.oneshot_info.cache_deactivate()
self.oneshot_info.cache_deactivate(self)

@memoize_when_activated
def oneshot_info(self):
Expand Down
4 changes: 2 additions & 2 deletions psutil/tests/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,14 +261,14 @@ def foo(self):

# activate
calls = []
f.foo.cache_activate()
f.foo.cache_activate(f)
f.foo()
f.foo()
self.assertEqual(len(calls), 1)

# deactivate
calls = []
f.foo.cache_deactivate()
f.foo.cache_deactivate(f)
f.foo()
f.foo()
self.assertEqual(len(calls), 2)
Expand Down
15 changes: 15 additions & 0 deletions psutil/tests/test_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -1195,6 +1195,21 @@ def test_oneshot_twice(self):
p.cpu_times()
self.assertEqual(m.call_count, 2)

def test_oneshot_cache(self):
# Make sure oneshot() cache is nonglobal. Instead it's
# supposed to be bound to the Process instance, see:
# https://github.com/giampaolo/psutil/issues/1373
p1, p2 = create_proc_children_pair()
p1_ppid = p1.ppid()
p2_ppid = p2.ppid()
self.assertNotEqual(p1_ppid, p2_ppid)
with p1.oneshot():
self.assertEqual(p1.ppid(), p1_ppid)
self.assertEqual(p2.ppid(), p2_ppid)
with p2.oneshot():
self.assertEqual(p1.ppid(), p1_ppid)
self.assertEqual(p2.ppid(), p2_ppid)

def test_halfway_terminated_process(self):
# Test that NoSuchProcess exception gets raised in case the
# process dies after we create the Process object.
Expand Down

0 comments on commit 2cdf81d

Please sign in to comment.