presto-main/src/main/java/io/prestosql/operator/aggregation/builder/SpillableHashAggregationBuilder.java/SpillableHashAggregationBuilder/buildResult
class SpillableHashAggregationBuilder:    @Override
    public WorkProcessor<Page> buildResult()
    {
        checkState(hasPreviousSpillCompletedSuccessfully(), "Previous spill hasn't yet finished");
        producingOutput = true;

        // Convert revocable memory to user memory as returned WorkProcessor holds on to memory so we no longer can revoke.
        if (localRevocableMemoryContext.getBytes() > 0) {
            long currentRevocableBytes = localRevocableMemoryContext.getBytes();
            localRevocableMemoryContext.setBytes(0);
            if (!localUserMemoryContext.trySetBytes(localUserMemoryContext.getBytes() + currentRevocableBytes)) {
                // TODO: this might fail (even though we have just released memory), but we don't
                // have a proper way to atomically convert memory reservations
                localRevocableMemoryContext.setBytes(currentRevocableBytes);
                // spill since revocable memory could not be converted to user memory immediately
                // TODO: this should be asynchronous
                getFutureValue(spillToDisk());
                updateMemory();
            }
        }

        if (!spiller.isPresent()) {
            return hashAggregationBuilder.buildResult();
        }

        if (shouldMergeWithMemory(getSizeInMemoryWhenUnspilling())) {
            return mergeFromDiskAndMemory();
        }
        else {
            getFutureValue(spillToDisk());
            return mergeFromDisk();
        }
    }