'Go through the list of requests and process the completed ones.
We take a snapshot in time of all requests and responses for this hunt. We
then process as many completed requests as possible. If responses are not
quite here we leave it for next time.
Args:
notification: The notification object that triggered this processing.
thread_pool: The thread pool to process the responses on.'

def ProcessCompletedRequests(self, notification, thread_pool):

    with queue_manager.QueueManager(token=self.token) as manager:
        for (request, _) in manager.FetchCompletedRequests(self.session_id, timestamp=(0, notification.timestamp)):
            if request.HasField('request'):
                manager.DeQueueClientRequest(request.client_id, request.request.task_id)
    processing = []
    while True:
        try:
            for (request, responses) in self.queue_manager.FetchCompletedResponses(self.session_id, timestamp=(0, notification.timestamp)):
                if ((request.id == 0) or (not responses)):
                    continue
                if (len(responses) != responses[(-1)].response_id):
                    if (request.transmission_count < 5):
                        stats.STATS.IncrementCounter('grr_request_retransmission_count')
                        request.transmission_count += 1
                        self.QueueRequest(request)
                    break
                self.hunt_obj.HeartBeat()
                self._Process(request, responses, thread_pool=thread_pool, events=processing)
                self.queue_manager.DeleteRequest(request)
                self.context.next_processed_request += 1
            return
        except queue_manager.MoreDataException:
            for event in processing:
                event.wait()
            self.FlushMessages()
            self.hunt_obj.Flush()
            continue
        finally:
            for event in processing:
                event.wait()
