private DocumentProcessor.Progress process(DocprocExecutor executor) {
        Iterator<Processing> iterator = processings.iterator();
        List<Tuple2<DocumentProcessor.Progress, Processing>> later = new ArrayList<>();
        while (iterator.hasNext()) {
            Processing processing = iterator.next();
            iterator.remove();
            if (requestContext.hasExpired()) {
                DocumentProcessor.Progress progress = DocumentProcessor.Progress.FAILED;
                final String location;
                if (processing != null) {
                    final CallStack callStack = processing.callStack();
                    if (callStack != null) {
                        final Call lastPopped = callStack.getLastPopped();
                        if (lastPopped != null) {
                            location = lastPopped.toString();
                        } else {
                            location = "empty call stack or no processors popped";
                        }
                    } else {
                        location = "no call stack";
                    }
                } else {
                    location = "no processing instance";
                }
                log.log(Level.FINE, () -> "Time is up for '" + processing + " failed, " + location + "'.");
                requestContext.processingFailed(RequestContext.ErrorCode.ERROR_PROCESSING_FAILURE, "Time is up.");
                return progress;
            }

            DocumentProcessor.Progress progress = DocumentProcessor.Progress.FAILED;
            try {
                progress = executor.process(processing);
            } catch (Exception e) {
                logProcessingFailure(processing, e);
                requestContext.processingFailed(e);
                return progress;
            }

            if (DocumentProcessor.Progress.LATER.equals(progress)) {
                later.add(new Tuple2<>(progress, processing));
            } else if (DocumentProcessor.Progress.DONE.equals(progress)) {
                processingsDone.add(processing);
            } else if (DocumentProcessor.Progress.FAILED.equals(progress)) {
                logProcessingFailure(processing, null);
                requestContext.processingFailed(RequestContext.ErrorCode.ERROR_PROCESSING_FAILURE,
                        progress.getReason().orElse("Document processing failed."));
                return progress;
            } else if (DocumentProcessor.Progress.PERMANENT_FAILURE.equals(progress)) {
                logProcessingFailure(processing, null);
                requestContext.processingFailed(RequestContext.ErrorCode.ERROR_PROCESSING_FAILURE,
                        progress.getReason().orElse("Document processing failed."));
                return progress;
            }
        }

        // Processings that have FAILED will have made this method terminate by now.
        // We now have successful Processings in 'processingsDone' and
        // the ones that have returned LATER in 'later'.

        if (!later.isEmpty()) {
            // Outdated comment:
            // "if this was a multioperationmessage and more than one of the processings returned LATER,
            // return the one with the lowest timeout:"
            // As multioperation is removed this can probably be simplified?
            DocumentProcessor.LaterProgress shortestDelay = (DocumentProcessor.LaterProgress) later.get(0).first;
            for (Tuple2<DocumentProcessor.Progress, Processing> tuple : later) {
                // re-add the LATER one to processings
                processings.add(tuple.second);
                // check to see if this one had a lower timeout than the previous one:
                if (((DocumentProcessor.LaterProgress) tuple.first).getDelay() < shortestDelay.getDelay()) {
                    shortestDelay = (DocumentProcessor.LaterProgress) tuple.first;
                }
            }
            return shortestDelay;
        } else {
            requestContext.processingDone(processingsDone);
            return DocumentProcessor.Progress.DONE;
        }
    }
Processes a single Processing, and fails the message if this processing fails.

@param executor the DocprocService to use for processing
docproc/src/main/java/com/yahoo/docproc/jdisc/DocumentProcessingTask.java