[tor-commits] [nyx/master] Move panel threading to subclass

atagar at torproject.org atagar at torproject.org
Mon Apr 18 20:23:16 UTC 2016


commit e0adea1ffcccf1cde046dfad698da7ce6f19b51d
Author: Damian Johnson <atagar at torproject.org>
Date:   Sun Apr 17 18:02:56 2016 -0700

    Move panel threading to subclass
    
    Three of our panels are also threads, performing actions at a set rate. All
    three have identical boilerplate so moving this to a DaemonPanel sublass of
    Panel.
    
    This rejiggers how the log panel works. The work it did to sleep minimal
    amounts of time was both silly and pointless.
---
 nyx/panel/__init__.py   | 46 ++++++++++++++++++++++++++++++++++++++++--
 nyx/panel/connection.py | 47 ++-----------------------------------------
 nyx/panel/header.py     | 37 ++--------------------------------
 nyx/panel/log.py        | 53 +++++++++----------------------------------------
 nyxrc.sample            |  4 ----
 5 files changed, 57 insertions(+), 130 deletions(-)

diff --git a/nyx/panel/__init__.py b/nyx/panel/__init__.py
index 028ba73..86e64ac 100644
--- a/nyx/panel/__init__.py
+++ b/nyx/panel/__init__.py
@@ -6,11 +6,12 @@ Panels consisting the nyx interface.
 """
 
 import collections
-import inspect
-import time
 import curses
 import curses.ascii
 import curses.textpad
+import inspect
+import threading
+import time
 
 import nyx.curses
 import stem.util.log
@@ -710,3 +711,44 @@ class Panel(object):
     self.addch(top, left + width - 1, curses.ACS_URCORNER, *attributes)
     self.addch(top + height - 1, left, curses.ACS_LLCORNER, *attributes)
     self.addch(top + height - 1, left + width - 1, curses.ACS_LRCORNER, *attributes)
+
+
+class DaemonPanel(Panel, threading.Thread):
+  def __init__(self, name, top = 0, left = 0, height = -1, width = -1, update_rate = 10):
+    Panel.__init__(self, name, top, left, height, width)
+    threading.Thread.__init__(self)
+    self.setDaemon(True)
+
+    self._pause_condition = threading.Condition()
+    self._halt = False  # terminates thread if true
+    self._update_rate = update_rate
+
+  def _update(self):
+    pass
+
+  def run(self):
+    """
+    Performs our _update() action at the given rate.
+    """
+
+    last_ran = -1
+
+    while not self._halt:
+      if self.is_paused() or (time.time() - last_ran) < self._update_rate:
+        with self._pause_condition:
+          if not self._halt:
+            self._pause_condition.wait(0.2)
+
+        continue  # done waiting, try again
+
+      self._update()
+      last_ran = time.time()
+
+  def stop(self):
+    """
+    Halts further resolutions and terminates the thread.
+    """
+
+    with self._pause_condition:
+      self._halt = True
+      self._pause_condition.notifyAll()
diff --git a/nyx/panel/connection.py b/nyx/panel/connection.py
index e7e80f5..b75376d 100644
--- a/nyx/panel/connection.py
+++ b/nyx/panel/connection.py
@@ -10,7 +10,6 @@ import time
 import collections
 import curses
 import itertools
-import threading
 
 import nyx.curses
 import nyx.panel
@@ -256,16 +255,14 @@ class CircuitEntry(Entry):
     return False
 
 
-class ConnectionPanel(nyx.panel.Panel, threading.Thread):
+class ConnectionPanel(nyx.panel.DaemonPanel):
   """
   Listing of connections tor is making, with information correlated against
   the current consensus and other data sources.
   """
 
   def __init__(self):
-    nyx.panel.Panel.__init__(self, 'connections')
-    threading.Thread.__init__(self)
-    self.setDaemon(True)
+    nyx.panel.DaemonPanel.__init__(self, 'connections', update_rate = UPDATE_RATE)
 
     self._scroller = nyx.curses.CursorScroller()
     self._entries = []            # last fetched display entries
@@ -274,9 +271,6 @@ class ConnectionPanel(nyx.panel.Panel, threading.Thread):
 
     self._last_resource_fetch = -1  # timestamp of the last ConnectionResolver results used
 
-    self._pause_condition = threading.Condition()
-    self._halt = False  # terminates thread if true
-
     # Tracks exiting port and client country statistics
 
     self._client_locale_usage = {}
@@ -317,34 +311,6 @@ class ConnectionPanel(nyx.panel.Panel, threading.Thread):
       self._sort_order = results
       self._entries = sorted(self._entries, key = lambda entry: [entry.sort_value(attr) for attr in self._sort_order])
 
-  def run(self):
-    """
-    Keeps connections listing updated, checking for new entries at a set rate.
-    """
-
-    last_ran = -1
-
-    while not self._halt:
-      if self.is_paused() or not tor_controller().is_alive() or (time.time() - last_ran) < UPDATE_RATE:
-        with self._pause_condition:
-          if not self._halt:
-            self._pause_condition.wait(0.2)
-
-        continue  # done waiting, try again
-
-      self._update()
-      self.redraw(True)
-
-      # TODO: The following is needed to show results *but* causes curses to
-      # flicker. For our plans on this see...
-      #
-      #   https://trac.torproject.org/projects/tor/ticket/18547#comment:1
-
-      # if last_ran == -1:
-      #   nyx.tracker.get_consensus_tracker().update(tor_controller().get_network_statuses([]))
-
-      last_ran = time.time()
-
   def key_handlers(self):
     def _scroll(key):
       page_height = self.get_preferred_size()[0] - 1
@@ -630,15 +596,6 @@ class ConnectionPanel(nyx.panel.Panel, threading.Thread):
       x = self.addstr(y, x, line.entry.get_type().upper(), BOLD, *attr)
       x = self.addstr(y, x, ')', *attr)
 
-  def stop(self):
-    """
-    Halts further resolutions and terminates the thread.
-    """
-
-    with self._pause_condition:
-      self._halt = True
-      self._pause_condition.notifyAll()
-
   def _update(self):
     """
     Fetches the newest resolved connections.
diff --git a/nyx/panel/header.py b/nyx/panel/header.py
index 164929b..df139f0 100644
--- a/nyx/panel/header.py
+++ b/nyx/panel/header.py
@@ -9,7 +9,6 @@ available.
 
 import os
 import time
-import threading
 
 import stem
 import stem.control
@@ -39,21 +38,16 @@ CONFIG = conf.config_dict('nyx', {
 })
 
 
-class HeaderPanel(nyx.panel.Panel, threading.Thread):
+class HeaderPanel(nyx.panel.DaemonPanel):
   """
   Top area containing tor settings and system information.
   """
 
   def __init__(self):
-    nyx.panel.Panel.__init__(self, 'header')
-    threading.Thread.__init__(self)
-    self.setDaemon(True)
-
+    nyx.panel.DaemonPanel.__init__(self, 'header', update_rate = UPDATE_RATE)
     self._vals = Sampling.create()
 
     self._last_width = nyx.curses.screen_size()[0]
-    self._pause_condition = threading.Condition()
-    self._halt = False  # terminates thread if true
     self._reported_inactive = False
 
     self._message = None
@@ -200,33 +194,6 @@ class HeaderPanel(nyx.panel.Panel, threading.Thread):
 
     _draw_status(subwindow, 0, subwindow.height - 1, self.is_paused(), self._message, *self._message_attr)
 
-  def run(self):
-    """
-    Keeps stats updated, checking for new information at a set rate.
-    """
-
-    last_ran = -1
-
-    while not self._halt:
-      if self.is_paused() or not self._vals.is_connected or (time.time() - last_ran) < UPDATE_RATE:
-        with self._pause_condition:
-          if not self._halt:
-            self._pause_condition.wait(0.2)
-
-        continue  # done waiting, try again
-
-      self._update()
-      last_ran = time.time()
-
-  def stop(self):
-    """
-    Halts further resolutions and terminates the thread.
-    """
-
-    with self._pause_condition:
-      self._halt = True
-      self._pause_condition.notifyAll()
-
   def reset_listener(self, controller, event_type, _):
     self._update()
 
diff --git a/nyx/panel/log.py b/nyx/panel/log.py
index cc8183b..1e1e87e 100644
--- a/nyx/panel/log.py
+++ b/nyx/panel/log.py
@@ -9,7 +9,6 @@ regular expressions.
 
 import os
 import time
-import threading
 
 import stem.response.events
 
@@ -28,8 +27,6 @@ from stem.util import conf, log
 def conf_handler(key, value):
   if key == 'features.log.prepopulateReadLimit':
     return max(0, value)
-  elif key == 'features.log.maxRefreshRate':
-    return max(10, value)
   elif key == 'cache.log_panel.size':
     return max(1000, value)
 
@@ -41,11 +38,12 @@ CONFIG = conf.config_dict('nyx', {
   'features.log.showDuplicateEntries': False,
   'features.log.prepopulate': True,
   'features.log.prepopulateReadLimit': 5000,
-  'features.log.maxRefreshRate': 300,
   'features.log.regex': [],
   'startup.events': 'N3',
 }, conf_handler)
 
+UPDATE_RATE = 0.3
+
 # The height of the drawn content is estimated based on the last time we redrew
 # the panel. It's chiefly used for scrolling and the bar indicating its
 # position. Letting the estimate be too inaccurate results in a display bug, so
@@ -61,16 +59,14 @@ NYX_LOGGER = log.LogBuffer(log.Runlevel.DEBUG, yield_records = True)
 stem_logger.addHandler(NYX_LOGGER)
 
 
-class LogPanel(nyx.panel.Panel, threading.Thread):
+class LogPanel(nyx.panel.DaemonPanel):
   """
   Listens for and displays tor, nyx, and stem events. This prepopulates
   from tor's log file if it exists.
   """
 
   def __init__(self):
-    nyx.panel.Panel.__init__(self, 'log')
-    threading.Thread.__init__(self)
-    self.setDaemon(True)
+    nyx.panel.DaemonPanel.__init__(self, 'log', update_rate = UPDATE_RATE)
 
     logged_events = nyx.arguments.expand_events(CONFIG['startup.events'])
     self._event_log = nyx.log.LogGroup(CONFIG['cache.log_panel.size'], group_by_day = True)
@@ -81,9 +77,8 @@ class LogPanel(nyx.panel.Panel, threading.Thread):
     self._show_duplicates = CONFIG['features.log.showDuplicateEntries']
 
     self._scroller = nyx.curses.Scroller()
-    self._halt = False  # terminates thread if true
-    self._pause_condition = threading.Condition()
     self._has_new_event = False
+    self._last_day = nyx.log.day_count(time.time())
 
     # fetches past tor events from log file, if available
 
@@ -359,46 +354,19 @@ class LogPanel(nyx.panel.Panel, threading.Thread):
 
     return y + 1
 
-  def run(self):
+  def _update(self):
     """
     Redraws the display, coalescing updates if events are rapidly logged (for
     instance running at the DEBUG runlevel) while also being immediately
     responsive if additions are less frequent.
     """
 
-    last_ran, last_day = -1, nyx.log.day_count(time.time())
-
-    while not self._halt:
-      current_day = nyx.log.day_count(time.time())
-      time_since_reset = time.time() - last_ran
-      max_log_update_rate = CONFIG['features.log.maxRefreshRate'] / 1000.0
-
-      sleep_time = 0
-
-      if (not self._has_new_event and last_day == current_day) or self.is_paused():
-        sleep_time = 5
-      elif time_since_reset < max_log_update_rate:
-        sleep_time = max(0.05, max_log_update_rate - time_since_reset)
-
-      if sleep_time:
-        with self._pause_condition:
-          if not self._halt:
-            self._pause_condition.wait(sleep_time)
-
-        continue
+    current_day = nyx.log.day_count(time.time())
 
-      last_ran, last_day = time.time(), current_day
+    if self._has_new_event or self._last_day != current_day:
+      self._last_day = current_day
       self.redraw(True)
 
-  def stop(self):
-    """
-    Halts further updates and terminates the thread.
-    """
-
-    with self._pause_condition:
-      self._halt = True
-      self._pause_condition.notifyAll()
-
   def _register_tor_event(self, event):
     msg = ' '.join(str(event).split(' ')[1:])
 
@@ -426,6 +394,3 @@ class LogPanel(nyx.panel.Panel, threading.Thread):
 
     if self._filter.match(event.display_message):
       self._has_new_event = True
-
-      with self._pause_condition:
-        self._pause_condition.notifyAll()
diff --git a/nyxrc.sample b/nyxrc.sample
index 612af6b..7bb300c 100644
--- a/nyxrc.sample
+++ b/nyxrc.sample
@@ -63,16 +63,12 @@ features.confirmQuit true
 # prepopulateReadLimit
 #   maximum entries read from the log file, used to prevent huge log files from
 #   causing a slow startup time.
-# maxRefreshRate
-#   rate limiting (in milliseconds) for drawing the log if updates are made
-#   rapidly (for instance, when at the DEBUG runlevel)
 # regex
 #   preconfigured regular expression pattern, up to five will be loaded
 
 features.log.showDuplicateEntries false
 features.log.prepopulate true
 features.log.prepopulateReadLimit 5000
-features.log.maxRefreshRate 300
 #features.log.regex My First Regex Pattern
 #features.log.regex ^My Second Regex Pattern$
 





More information about the tor-commits mailing list