private void handleVisitorInfoMessage(VisitorInfoMessage msg) {

        Reply reply = msg.createReply();
        msg.swapState(reply);

        if (log.isLoggable(Level.FINE)) {
            log.log(Level.FINE, "Visitor session " + sessionName +
                    ": Received VisitorInfo with " +
                    msg.getFinishedBuckets().size() + " finished buckets");
        }

        try {
            if (!msg.getErrorMessage().isEmpty()) {
                params.getControlHandler().onVisitorError(msg.getErrorMessage());
            }
            synchronized (stateMonitor) {
                // NOTE: control handlers shall sync on token themselves (aka. `stateMonitor`)
                // if they want to access it, but recursive locking is OK and by
                // always locking we make screwing it up harder.
                if (state.getState() == State.WORKING) {
                    params.getControlHandler().onProgress(progress.getToken());
                } else {
                    reply.addError(new Error(ErrorCode.APP_FATAL_ERROR, "Visitor has been shut down"));
                }
            }
        } catch (Exception e) {
            handleMessageProcessingException(reply, e, "VisitorInfoMessage");
        }  finally {
            receiver.reply(reply);
        }
    }
NOTE: not called from within lock, function must take lock itself
documentapi/src/main/java/com/yahoo/documentapi/messagebus/MessageBusVisitorSession.java