[PATCH] rtemstoolkit: Rework the log locking adding an output thread.
chrisj at rtems.org
chrisj at rtems.org
Fri May 8 07:59:41 UTC 2020
From: Chris Johns <chrisj at rtems.org>
---
rtemstoolkit/log.py | 76 +++++++++++++++++++++++++++++----------------
1 file changed, 49 insertions(+), 27 deletions(-)
diff --git a/rtemstoolkit/log.py b/rtemstoolkit/log.py
index 00fdb05..2e73e72 100755
--- a/rtemstoolkit/log.py
+++ b/rtemstoolkit/log.py
@@ -34,8 +34,11 @@
from __future__ import print_function
+import atexit
import os
+import queue
import sys
+import time
import threading
from rtemstoolkit import error
@@ -57,9 +60,47 @@ tracing = False
quiet = False
#
-# Global output lock to keep output together when working with threads
+# Run the output from a separate thread to avoid blocking with a
+# lock. We want the outout to be blocks.
#
-lock = threading.Lock()
+_output_queue = queue.SimpleQueue()
+
+def _output_worker(timeout=None):
+ try:
+ is_write, is_flush, handle, out = _output_queue.get(timeout=timeout)
+ except queue.Empty:
+ return timeout is None
+ except KeyboardInterrupt:
+ return False
+ except:
+ return False
+ if is_write:
+ handle.write(out)
+ else:
+ for l in out.replace(chr(13), '').splitlines():
+ print(l, file=handle)
+ if is_flush:
+ handle.flush()
+ return True
+
+def _output_put(is_write, is_flush, handle, out):
+ _output_queue.put((is_write, is_flush, handle, out))
+ while not _output_queue.empty():
+ time.sleep(0.001)
+
+def _output_exiting():
+ while _output_worker(timeout=0.100):
+ pass
+
+def _output_thread():
+ while _output_worker():
+ pass
+
+_outputter = threading.Thread(target=_output_thread,
+ daemon=True,
+ name='log_outputter')
+_outputter.start()
+atexit.register(_output_exiting)
def info(args):
s = [' Command Line: %s' % (' '.join(args))]
@@ -92,20 +133,12 @@ def _output(text = os.linesep, log = None):
elif default is not None:
default.output(text)
else:
- lock.acquire()
- for l in text.replace(chr(13), '').splitlines():
- print(l)
- lock.release()
+ _output_put(False, False, sys.stdout, text)
if capture is not None:
- lock.acquire()
capture(text)
- lock.release()
def stderr(text = os.linesep, log = None):
- lock.acquire()
- for l in text.replace(chr(13), '').splitlines():
- print(l, file = sys.stderr)
- lock.release()
+ _output_put(False, False, sys.stderr, text)
def output(text = os.linesep, log = None):
if not quiet:
@@ -114,10 +147,7 @@ def output(text = os.linesep, log = None):
def notice(text = os.linesep, log = None, stdout_only = False):
if not quiet and \
(default is not None and not default.has_stdout() or stdout_only):
- lock.acquire()
- for l in text.replace(chr(13), '').splitlines():
- print(l)
- lock.release()
+ _output_put(False, False, sys.stdout, text)
if not stdout_only:
_output(text, log)
@@ -138,7 +168,6 @@ def flush(log = None):
class log:
"""Log output to stdout or a file."""
def __init__(self, streams = None, tail_size = 100):
- self.lock = threading.Lock()
self.tail = []
self.tail_size = tail_size
self.fhs = [None, None]
@@ -186,16 +215,9 @@ class log:
out = os.linesep.join(text) + os.linesep
if isinstance(out, bytes):
out = out.decode('utf-8', 'ignore')
- self.lock.acquire()
- try:
- for f in range(0, len(self.fhs)):
- if self.fhs[f] is not None:
- self.fhs[f].write(out)
- self.flush()
- except:
- raise
- finally:
- self.lock.release()
+ for f in range(0, len(self.fhs)):
+ if self.fhs[f] is not None:
+ _output_put(True, True, self.fhs[f], out)
def flush(self):
"""Flush the output."""
--
2.24.1
More information about the devel
mailing list