def process_request(self, num_pending, ports, request_blocking=False):
        '''port is the open port at the worker, num_pending is the num_pending of stack.
        A non-zero num_pending means that the worker is pending on something while
        looking for new job, so the worker should not be killed.
        '''
        if any(port in self._step_requests for port in ports):
            # if the port is available
            port = [x for x in ports if x in self._step_requests][0]
            self._worker_backend_socket.send_pyobj(
                self._step_requests.pop(port))
            self._n_processed += 1
            self.report(f'Step {port} processed')
            # port should be in claimed ports
            self._claimed_ports.remove(port)
            if ports[0] in self._last_pending_time:
                self._last_pending_time.pop(ports[0])
        elif any(port in self._claimed_ports for port in ports):
            # the port is claimed, but the real message is not yet available
            self._worker_backend_socket.send_pyobj({})
            self.report(f'pending with claimed {ports}')
        elif any(port in self._blocking_ports for port in ports):
            # in block list but appear to be idle, kill it
            self._max_workers -= 1
            env.logger.debug(
                f'Reduce maximum number of workers to {self._max_workers} after completion of a blocking subworkflow.'
            )
            for port in ports:
                if port in self._blocking_ports:
                    self._blocking_ports.remove(port)
                if port in self._available_ports:
                    self._available_ports.remove(port)
            self._worker_backend_socket.send_pyobj(None)
            self._num_workers -= 1
            self.report(f'Blocking worker {ports} killed')
        elif self._substep_requests:
            # port is not claimed, free to use for substep worker
            msg = self._substep_requests.pop()
            self._worker_backend_socket.send_pyobj(msg)
            self._n_processed += 1
            self.report(f'Substep processed with {ports[0]}')
            # port can however be in available ports
            for port in ports:
                if port in self._available_ports:
                    self._available_ports.remove(port)
                if port in self._last_pending_time:
                    self._last_pending_time.pop(port)
        elif request_blocking:
            self._worker_backend_socket.send_pyobj({})
            return ports[0]
        elif num_pending == 0 and ports[
                0] in self._last_pending_time and time.time(
                ) - self._last_pending_time[ports[0]] > 5:
            # kill the worker
            for port in ports:
                if port in self._available_ports:
                    self._available_ports.remove(port)
            self._worker_backend_socket.send_pyobj(None)
            self._num_workers -= 1
            self.report(f'Kill standing {ports}')
            self._last_pending_time.pop(ports[0])
        else:
            if num_pending == 0 and ports[0] not in self._last_pending_time:
                self._last_pending_time[ports[0]] = time.time()
            self._available_ports.add(ports[0])
            self._worker_backend_socket.send_pyobj({})
            ports = tuple(ports)
            if (ports, num_pending) not in self._last_pending_msg or time.time(
            ) - self._last_pending_msg[(ports, num_pending)] > 1.0:
                self.report(
                    f'pending with port {ports} at num_pending {num_pending}')
                self._last_pending_msg[(ports, num_pending)] = time.time()
port is the open port at the worker, num_pending is the num_pending of stack.
        A non-zero num_pending means that the worker is pending on something while
        looking for new job, so the worker should not be killed.