All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Alex Bennée" <alex.bennee@linaro.org>
To: Robert Foley <robert.foley@linaro.org>
Cc: peter.puhov@linaro.org, Cleber Rosa <crosa@redhat.com>,
	jsnow@redhat.com, qemu-devel@nongnu.org,
	Eduardo Habkost <ehabkost@redhat.com>
Subject: Re: [PATCH 2/2] python/qemu: Change ConsoleSocket to optionally drain socket.
Date: Thu, 16 Jul 2020 14:42:29 +0100	[thread overview]
Message-ID: <875zanwp8a.fsf@linaro.org> (raw)
In-Reply-To: <20200715204814.2630-3-robert.foley@linaro.org>


Robert Foley <robert.foley@linaro.org> writes:

> The primary purpose of this change is to clean up
> machine.py's console_socket property to return a single type,
> a ConsoleSocket.
>
> ConsoleSocket now derives from a socket, which means that
> in the default case (of not draining), machine.py
> will see the same behavior as it did prior to ConsoleSocket.
>
> Signed-off-by: Robert Foley <robert.foley@linaro.org>
> ---
>  python/qemu/console_socket.py | 81 +++++++++++++++++++++--------------
>  python/qemu/machine.py        | 13 ++----
>  2 files changed, 54 insertions(+), 40 deletions(-)
>
> diff --git a/python/qemu/console_socket.py b/python/qemu/console_socket.py
> index 6a746c1dbf..475de5b101 100644
> --- a/python/qemu/console_socket.py
> +++ b/python/qemu/console_socket.py
> @@ -13,68 +13,76 @@ which can drain a socket and optionally dump the bytes to file.
>  # the COPYING file in the top-level directory.
>  #
>  
> -import asyncore
>  import socket
>  import threading
>  from collections import deque
>  import time
>  
>  
> -class ConsoleSocket(asyncore.dispatcher):
> +class ConsoleSocket(socket.socket):
>      """
>      ConsoleSocket represents a socket attached to a char device.
>  
> -    Drains the socket and places the bytes into an in memory buffer
> -    for later processing.
> +    Optionally (if drain==True), drains the socket and places the bytes
> +    into an in memory buffer for later processing.
>  
>      Optionally a file path can be passed in and we will also
>      dump the characters to this file for debugging purposes.
>      """
> -    def __init__(self, address, file=None):
> +    def __init__(self, address, file=None, drain=False):
>          self._recv_timeout_sec = 300
>          self._sleep_time = 0.5
>          self._buffer = deque()
> -        self._asyncore_thread = None
> -        self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
> -        self._sock.connect(address)
> +        self._drain_thread = None
> +        socket.socket.__init__(self, socket.AF_UNIX, socket.SOCK_STREAM)
> +        self.connect(address)
> +        self._drain = drain

We end up with two variables that represent the fact we have draining
happening. Could we rationalise it into:

  if drain:
     self._drain_thread = self._thread_start()
  else
     self._drain_thread = None # if this is needed

And then tests for:

  if not self._drain:

become

  if self._drain_thread is None:

>          self._logfile = None
>          if file:
>              self._logfile = open(file, "w")
> -        asyncore.dispatcher.__init__(self, sock=self._sock)
>          self._open = True
> -        self._thread_start()
> +        if drain:
> +            self._thread_start()
> +
> +    def _drain_fn(self):
> +        """Drains the socket and runs while the socket is open."""
> +        while self._open:
> +            try:
> +                self._drain_socket()
> +            except socket.timeout:
> +                # The socket is expected to timeout since we set a
> +                # short timeout to allow the thread to exit when
> +                # self._open is set to False.
> +                time.sleep(self._sleep_time)
>  
>      def _thread_start(self):
> -        """Kick off a thread to wait on the asyncore.loop"""
> -        if self._asyncore_thread is not None:
> +        """Kick off a thread to drain the socket."""
> +        if self._drain_thread is not None:
>              return
> -        self._asyncore_thread = threading.Thread(target=asyncore.loop,
> -                                                 kwargs={'timeout':1})
> -        self._asyncore_thread.daemon = True
> -        self._asyncore_thread.start()
> -
> -    def handle_close(self):
> -        """redirect close to base class"""
> -        # Call the base class close, but not self.close() since
> -        # handle_close() occurs in the context of the thread which
> -        # self.close() attempts to join.
> -        asyncore.dispatcher.close(self)
> +        # Configure socket to not block and timeout.
> +        # This allows our drain thread to not block
> +        # on recieve and exit smoothly.
> +        socket.socket.setblocking(self, 0)
> +        socket.socket.settimeout(self, 1)
> +        self._drain_thread = threading.Thread(target=self._drain_fn)
> +        self._drain_thread.daemon = True
> +        self._drain_thread.start()
>  
>      def close(self):
>          """Close the base object and wait for the thread to terminate"""
>          if self._open:
>              self._open = False
> -            asyncore.dispatcher.close(self)
> -            if self._asyncore_thread is not None:
> -                thread, self._asyncore_thread = self._asyncore_thread, None
> +            if self._drain and self._drain_thread is not None:
> +                thread, self._drain_thread = self._drain_thread, None

Would self._drain ever not have self._drain_thread set?

>                  thread.join()
> +            socket.socket.close(self)
<snip>
> diff --git a/python/qemu/machine.py b/python/qemu/machine.py
> index 6769359766..62709d86e4 100644
> --- a/python/qemu/machine.py
> +++ b/python/qemu/machine.py
> @@ -22,7 +22,6 @@ import logging
>  import os
>  import subprocess
>  import shutil
> -import socket

FYI minor patch conflict here with master

>  import tempfile
>  from typing import Optional, Type
>  from types import TracebackType
> @@ -591,12 +590,8 @@ class QEMUMachine:
>          Returns a socket connected to the console
>          """
>          if self._console_socket is None:
> -            if self._drain_console:
> -                self._console_socket = console_socket.ConsoleSocket(
> -                    self._console_address,
> -                    file=self._console_log_path)
> -            else:
> -                self._console_socket = socket.socket(socket.AF_UNIX,
> -                                                     socket.SOCK_STREAM)
> -                self._console_socket.connect(self._console_address)
> +            self._console_socket = console_socket.ConsoleSocket(
> +                self._console_address,
> +                file=self._console_log_path,
> +                drain=self._drain_console)
>          return self._console_socket


-- 
Alex Bennée


  reply	other threads:[~2020-07-16 13:43 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-07-15 20:48 [PATCH 0/2] python/qemu: follow-up changes for ConsoleSocket Robert Foley
2020-07-15 20:48 ` [PATCH 1/2] python/qemu: Cleanup changes to ConsoleSocket Robert Foley
2020-07-16 11:07   ` Alex Bennée
2020-07-16 12:50     ` Robert Foley
2020-07-16 13:13       ` Alex Bennée
2020-07-16 13:42         ` Philippe Mathieu-Daudé
2020-07-15 20:48 ` [PATCH 2/2] python/qemu: Change ConsoleSocket to optionally drain socket Robert Foley
2020-07-16 13:42   ` Alex Bennée [this message]
2020-07-16 17:05     ` Robert Foley

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=875zanwp8a.fsf@linaro.org \
    --to=alex.bennee@linaro.org \
    --cc=crosa@redhat.com \
    --cc=ehabkost@redhat.com \
    --cc=jsnow@redhat.com \
    --cc=peter.puhov@linaro.org \
    --cc=qemu-devel@nongnu.org \
    --cc=robert.foley@linaro.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.