[elbe-devel] [PATCH] log: Fix async_logging()

Olivier Dion dion at linutronix.de
Wed Jun 3 00:12:24 CEST 2020


There was a nasty bug in async_logging() that make it spin forever and
burn time on the cpu.  Basically after the pipe breaks itself, the
thread would enter an infinite loop if there's no newline terminating
the stream.

This commit fixes that by removing the epoll stuff and detecting the
broken pipe by reading the empty string from it.

Also since we're there, I augmented the atmost size up to 4096 bytes
instead of 80.  This should make logging a little more fluid.

Signed-off-by: Olivier Dion <dion at linutronix.de>
---
 elbepack/log.py | 57 +++++++++++++++++++++++--------------------------
 1 file changed, 27 insertions(+), 30 deletions(-)

diff --git a/elbepack/log.py b/elbepack/log.py
index f17df9e7..46ade295 100644
--- a/elbepack/log.py
+++ b/elbepack/log.py
@@ -7,7 +7,6 @@
 import collections
 import logging
 import os
-import select
 import threading
 from contextlib import contextmanager
 
@@ -220,7 +219,6 @@ class AsyncLogging(object):
 
     def __init__(self, atmost, stream, block):
         self.lines = []
-        self.epoll = select.epoll()
         self.atmost = atmost
         self.fd = None
         calling_thread = threading.current_thread().ident
@@ -231,7 +229,6 @@ class AsyncLogging(object):
 
     def __call__(self, r, w):
         os.close(w)
-        self.epoll.register(r, select.EPOLLIN | select.EPOLLHUP)
         self.fd = r
         try:
             self.run()
@@ -239,41 +236,41 @@ class AsyncLogging(object):
             os.close(r)
 
     def run(self):
-        alive = True
         rest = bytes()
-        while alive:
-            events = self.epoll.poll()
-            for _, event in events:
-                if event & select.EPOLLIN:
-                    rest = self.read(rest)
-                if event & select.EPOLLHUP:
-                    alive = False
-
-        # Reading rest after pipe hang up
+
         while True:
-            rest = self.read(rest)
-            if not rest:
+
+            buf  = os.read(self.fd, self.atmost)
+
+            # Pipe broke
+            if not buf:
                 break
 
+            buf = rest + buf
+            cnt = 0
+            j   = 0
+
+            # Line buffering
+            for i in range(len(buf)):
+                if buf[i] == '\n':
+                    self.lines.append(buf[j:i])
+                    cnt += 1
+                    j = i + 1
+
+            # Log the line now for echo back
+            if cnt:
+                self.stream.info("\n".join(self.lines[-cnt:]))
+
+            # Keep rest for next line buffering
+            rest = buf[j:]
+
         if self.lines:
             self.lines[-1] += rest
             self.block.info("\n".join(self.lines))
 
-    def read(self, rest):
-        buff = rest + os.read(self.fd, self.atmost)
-        j = 0
-        count = 0
-        for i in range(len(buff)):
-            if buff[i] == '\n':
-                self.lines.append(buff[j:i])
-                count += 1
-                j = i + 1
-        if count:
-            self.stream.info("\n".join(self.lines[-count:]))
-        return buff[j:]
-
-
-def async_logging(r, w, stream, block, atmost=80):
+
+
+def async_logging(r, w, stream, block, atmost=4096):
     t = threading.Thread(target=AsyncLogging(atmost, stream, block),
                          args=(r, w))
     t.daemon = True
-- 
2.27.0




More information about the elbe-devel mailing list