def terminate(self):
        '''Stop the server process and change our state to TERMINATING. Only valid if state=READY.'''
        logger.debug('client.terminate() called (state=%s)', self.strstate)
        if self.state == ClientState.WAITING_FOR_RESULT:
            raise ClientStateError('terimate() called while state='+self.strstate)
        if self.state == ClientState.TERMINATING:
            raise ClientStateError('terimate() called while state='+self.strstate)
        elif self.state in ClientState.TerminatedSet:
            assert not self._server_process.is_alive()
            return
        elif self.state == ClientState.READY:
            # Check that the process itself is still alive
            self._assert_alive()

            # Make sure the SIGCHLD signal handler doesn't throw any exceptions
            self.state = ClientState.TERMINATING

            # Do not call execute() because that function will check
            # whether the process is alive and throw an exception if not
            # TODO: can the queue itself throw exceptions?
            self._delegate_channel.put(FunctionCallDelegate(_raise_terminate))

            # Wait for acknowledgement
            try:
                self._read_result(num_retries=5)
            except ProcessTerminationError as ex:
                pass
            except ChannelError as ex:
                # Was interrupted five times in a row! Ignore for now
                logger.debug('client failed to read sentinel from channel after 5 retries - will terminate anyway')

            self.state = ClientState.TERMINATED_CLEANLY
Stop the server process and change our state to TERMINATING. Only valid if state=READY.