[nyx/master] Replace pause conditionals with sleep

commit 2bac3dc5831be7ad6219fd915de3656f1bc38437 Author: Damian Johnson <atagar@torproject.org> Date: Sat Sep 30 12:24:09 2017 -0700 Replace pause conditionals with sleep Threading conditionals are the proper method of doing an interruptable sleep, so nyx can shut down right away when quit. However, each of these conditionals impose a surprisingly high tax in terms of baseline cpu usage. Fully half the cpu when idle is just spent on checking conditionals. Guess they're implemented with some sort of busy puller... We don't need interruptability. It's nice, but having our interface take a fraction of a second is well worth halving our cpu usage. When idle this change drops cpu usage from 2.1% to 1.1% for me. Pause Cpu 0.1s 1.4% 0.2s 1.3% 0.3s 1.2% 0.4s 1.1% 0.5s 1.1% 0.6s 1.1% --- nyx/__init__.py | 6 ++++++ nyx/panel/__init__.py | 18 ++++++++---------- nyx/panel/connection.py | 8 +++----- nyx/tracker.py | 18 ++++++------------ 4 files changed, 23 insertions(+), 27 deletions(-) diff --git a/nyx/__init__.py b/nyx/__init__.py index 7475878..c326cd3 100644 --- a/nyx/__init__.py +++ b/nyx/__init__.py @@ -112,6 +112,12 @@ stem.control.CACHEABLE_GETINFO_PARAMS = list(stem.control.CACHEABLE_GETINFO_PARA stem.control.LOG_CACHE_FETCHES = False +# Duration for threads to pause when there's no work left to do. This is a +# compromise - lower means fater shutdown when quit but higher means lower cpu +# usage when running. + +PAUSE_TIME = 0.4 + SCHEMA_VERSION = 2 # version of our scheme, bump this if you change the following SCHEMA = ( 'CREATE TABLE schema(version INTEGER)', diff --git a/nyx/panel/__init__.py b/nyx/panel/__init__.py index 2139634..3e1aa61 100644 --- a/nyx/panel/__init__.py +++ b/nyx/panel/__init__.py @@ -33,7 +33,7 @@ import time import nyx.curses -from nyx import nyx_interface +from nyx import PAUSE_TIME, nyx_interface __all__ = [ 'config', @@ -189,7 +189,6 @@ class DaemonPanel(Panel, threading.Thread): threading.Thread.__init__(self) self.setDaemon(True) - self._pause_condition = threading.Condition() self._halt = False # terminates thread if true self._update_rate = update_rate @@ -201,13 +200,14 @@ class DaemonPanel(Panel, threading.Thread): Performs our _update() action at the given rate. """ - last_ran = -1 + last_ran = None while not self._halt: - if nyx_interface().is_paused() or (time.time() - last_ran) < self._update_rate: - with self._pause_condition: - if not self._halt: - self._pause_condition.wait(max(0.2, self._update_rate - 0.01)) + if nyx_interface().is_paused() or (last_ran and time.time() - last_ran < self._update_rate): + sleep_until = last_ran + self._update_rate + 0.1 + + while not self._halt and time.time() < sleep_until: + time.sleep(PAUSE_TIME) continue # done waiting, try again @@ -219,6 +219,4 @@ class DaemonPanel(Panel, threading.Thread): Halts further resolutions and terminates the thread. """ - with self._pause_condition: - self._halt = True - self._pause_condition.notifyAll() + self._halt = True diff --git a/nyx/panel/connection.py b/nyx/panel/connection.py index 670486c..c172908 100644 --- a/nyx/panel/connection.py +++ b/nyx/panel/connection.py @@ -16,7 +16,7 @@ import nyx.panel import nyx.popups import nyx.tracker -from nyx import nyx_interface, tor_controller +from nyx import PAUSE_TIME, nyx_interface, tor_controller from nyx.curses import WHITE, NORMAL, BOLD, HIGHLIGHT from nyx.menu import MenuItem, Submenu, RadioMenuItem, RadioGroup @@ -472,10 +472,6 @@ class ConnectionPanel(nyx.panel.DaemonPanel): start_time = time.time() while True: - with self._pause_condition: - if not self._halt: - self._pause_condition.wait(0.5) - resolution_count = conn_resolver.run_counter() if resolution_count != 0: @@ -484,6 +480,8 @@ class ConnectionPanel(nyx.panel.DaemonPanel): break elif self._halt: return + else: + time.sleep(PAUSE_TIME) controller = tor_controller() LAST_RETRIEVED_CIRCUITS = controller.get_circuits([]) diff --git a/nyx/tracker.py b/nyx/tracker.py index 9f1d72d..b2190fe 100644 --- a/nyx/tracker.py +++ b/nyx/tracker.py @@ -60,7 +60,7 @@ import stem.control import stem.descriptor.router_status_entry import stem.util.log -from nyx import tor_controller +from nyx import PAUSE_TIME, tor_controller from stem.util import conf, connection, enum, proc, str_tools, system CONFIG = conf.config_dict('nyx', { @@ -378,7 +378,6 @@ class Daemon(threading.Thread): self._run_counter = 0 # counter for the number of successful runs self._is_paused = False - self._pause_condition = threading.Condition() self._halt = False # terminates thread if true controller = tor_controller() @@ -387,14 +386,11 @@ class Daemon(threading.Thread): def run(self): while not self._halt: - time_since_last_ran = time.time() - self._last_ran + if self._is_paused or time.time() - self._last_ran < self._rate: + sleep_until = self._last_ran + self._rate + 0.1 - if self._is_paused or time_since_last_ran < self._rate: - sleep_duration = max(0.02, self._rate - time_since_last_ran) - - with self._pause_condition: - if not self._halt: - self._pause_condition.wait(sleep_duration) + while not self._halt and time.time() < sleep_until: + time.sleep(PAUSE_TIME) continue # done waiting, try again @@ -465,9 +461,7 @@ class Daemon(threading.Thread): Halts further work and terminates the thread. """ - with self._pause_condition: - self._halt = True - self._pause_condition.notifyAll() + self._halt = True def _tor_status_listener(self, controller, event_type, _): with self._process_lock:
participants (1)
-
atagar@torproject.org