[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