nixos/lib/test-driver: clean up threads correctly

The current implementation just forks off a thread to read
QEMU's stdout and lets it exist forever. This, however,
makes the interpreter shutdown racy, as the thread could
still be running and writing out buffered stdout when the
main thread exits (and since it's using the low level API,
the worker thread does not get cleaned up by the atexit hooks
installed by `threading`, either). So, instead of doing that,
let's create a real `threading.Thread` object, and also
explicitly `join` it along with the other stuff when cleaning up.
This commit is contained in:
K900 2021-10-19 15:42:27 +03:00
parent 0c084c89fe
commit a874235dff

View file

@ -6,7 +6,7 @@ from xml.sax.saxutils import XMLGenerator
from colorama import Style from colorama import Style
import queue import queue
import io import io
import _thread import threading
import argparse import argparse
import atexit import atexit
import base64 import base64
@ -409,6 +409,7 @@ class Machine:
pid: Optional[int] = None pid: Optional[int] = None
monitor: Optional[socket.socket] = None monitor: Optional[socket.socket] = None
shell: Optional[socket.socket] = None shell: Optional[socket.socket] = None
serial_thread: Optional[threading.Thread]
booted: bool = False booted: bool = False
connected: bool = False connected: bool = False
@ -444,6 +445,8 @@ class Machine:
self.cleanup_statedir() self.cleanup_statedir()
self.state_dir.mkdir(mode=0o700, exist_ok=True) self.state_dir.mkdir(mode=0o700, exist_ok=True)
self.serial_thread = None
@staticmethod @staticmethod
def create_startcommand(args: Dict[str, str]) -> StartCommand: def create_startcommand(args: Dict[str, str]) -> StartCommand:
rootlog.warning( rootlog.warning(
@ -921,7 +924,8 @@ class Machine:
self.last_lines.put(line) self.last_lines.put(line)
self.log_serial(line) self.log_serial(line)
_thread.start_new_thread(process_serial_output, ()) self.serial_thread = threading.Thread(target=process_serial_output)
self.serial_thread.start()
self.wait_for_monitor_prompt() self.wait_for_monitor_prompt()
@ -1021,9 +1025,12 @@ class Machine:
assert self.process assert self.process
assert self.shell assert self.shell
assert self.monitor assert self.monitor
assert self.serial_thread
self.process.terminate() self.process.terminate()
self.shell.close() self.shell.close()
self.monitor.close() self.monitor.close()
self.serial_thread.join()
class VLan: class VLan: