presto-main/src/main/java/io/prestosql/operator/LookupJoinOperator.java/JoinProcessor/getOutput
class JoinProcessor:        private Page getOutput()
        {
            // TODO introduce explicit state (enum), like in HBO

            if (!spillInProgress.isDone()) {
                /*
                 * We cannot produce output when there is some previous input spilling. This is because getOutput() may result in additional portion of input being spilled
                 * (when spilling state has changed in partitioned lookup source since last time) and spiller does not allow multiple concurrent spills.
                 */
                return null;
            }
            checkSuccess(spillInProgress, "spilling failed");

            if (probe == null && pageBuilder.isEmpty() && !finishing) {
                return null;
            }

            if (!tryFetchLookupSourceProvider()) {
                if (!finishing) {
                    return null;
                }

                verify(finishing);
                // We are no longer interested in the build side (the lookupSourceProviderFuture's value).
                addSuccessCallback(lookupSourceProviderFuture, LookupSourceProvider::close);
                lookupSourceProvider = new StaticLookupSourceProvider(new EmptyLookupSource());
            }

            if (probe == null && finishing && !unspilling) {
                /*
                 * We do not have input probe and we won't have any, as we're finishing.
                 * Let LookupSourceFactory know LookupSources can be disposed as far as we're concerned.
                 */
                verify(partitionedConsumption == null, "partitioned consumption already started");
                partitionedConsumption = lookupSourceFactory.finishProbeOperator(lookupJoinsCount);
                unspilling = true;
            }

            if (probe == null && unspilling && !finished) {
                /*
                 * If no current partition or it was exhausted, unspill next one.
                 * Add input there when it needs one, produce output. Be Happy.
                 */
                tryUnspillNext();
            }

            if (probe != null) {
                processProbe();
            }

            if (outputPage != null) {
                verify(pageBuilder.isEmpty());
                Page output = outputPage;
                outputPage = null;
                return output;
            }

            // It is impossible to have probe == null && !pageBuilder.isEmpty(),
            // because we will flush a page whenever we reach the probe end
            verify(probe != null || pageBuilder.isEmpty());
            return null;
        }