[tor-commits] [nyx/master] Replace pause conditionals with sleep

atagar at torproject.org atagar at torproject.org
Sun Oct 1 23:14:44 UTC 2017


commit 2bac3dc5831be7ad6219fd915de3656f1bc38437
Author: Damian Johnson <atagar at 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:



More information about the tor-commits mailing list