示例#1
0
 def enter_main_loop(self):
     """Create and maintain active channels for all running guests."""
     with GracefulShutdown():
         try:
             while True:
                 self.update_workers()
                 time.sleep(10)
         finally:
             for channel in self.workers.values():
                 channel.terminate()
    def raw_readline(self):
        """
        Read a newline terminated string from the remote side.

        This method overrides the
        :py:func:`~negotiator_common.NegotiatorInterface.raw_readline()` method
        of the :py:func:`~negotiator_common.NegotiatorInterface` class to
        implement blocking reads based on :py:data:`os.O_ASYNC` and
        :py:data:`signal.SIGIO` (see also :py:class:`WaitForRead`).

        :returns: The data read from the remote side (a string).
        """
        while True:
            # Check if the channel contains data.
            logger.debug("Preparing to read line from %s ..", self.conn_label)
            data = self.conn_handle.readline()
            if data:
                break
            # If the readline() above returns an empty string the channel
            # is (probably) not connected. At this point we'll bother to
            # prepare a convoluted way to block until the channel does
            # become connected.
            logger.debug("Got an empty read, emulating blocking read of %s ..",
                         self.conn_label)
            # Set the O_ASYNC flag on the file descriptor connected to the
            # character device (this is required to use SIGIO signals).
            flags = fcntl.fcntl(self.conn_handle, fcntl.F_GETFL)
            fcntl.fcntl(self.conn_handle, fcntl.F_SETFL, flags | os.O_ASYNC)
            # Spawn a subprocess to reliably handle SIGIO signals. Due to the
            # nature of (SIGIO) signals more than one signal may be delivered
            # and this is a big problem when you want to do more than just call
            # sys.exit(). The alternative to this would be signal.pause() but
            # that function has an inherent race condition. To fix that race
            # condition there is sigsuspend() but this function is not
            # available in the Python standard library.
            waiter = WaitForRead()
            # If we get killed we need to make sure we take the subprocess
            # down with us, otherwise the subprocess may still be reading
            # from the character device when we are restarted and that's a
            # problem because the character device doesn't allow multiple
            # readers; all but the first reader will get the error
            # `IOError: [Errno 16] Device or resource busy'.
            with GracefulShutdown():
                try:
                    # Start the subprocess.
                    waiter.start()
                    # Connect the file descriptor to the subprocess.
                    fcntl.fcntl(self.conn_handle, fcntl.F_SETOWN, waiter.pid)
                    # The channel may have become connected after we last got an empty
                    # read but before we spawned our subprocess, so check one more
                    # time to make sure.
                    data = self.conn_handle.readline()
                    if data:
                        break
                    # If there is still no data available we'll wait for the
                    # subprocess to indicate that data has become available.
                    waiter.join()
                    # Let's see if the subprocess is right :-)
                    data = self.conn_handle.readline()
                    if data:
                        break
                finally:
                    logger.debug(
                        "Terminating subprocess with process id %i ..",
                        waiter.pid)
                    waiter.terminate()
            # If the convoluted way to simulate blocking reads above ever
            # fails we don't want this method to turn into a `busy loop'.
            logger.debug(
                "Blocking read emulation seems to have failed, falling back to 1 second polling interval .."
            )
            time.sleep(1)
        logger.debug("Read %i bytes from %s: %r", len(data), self.conn_label,
                     data)
        return data