com.facebook.buck.parser.PipelineNodeCache.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.parser.PipelineNodeCache.java

Source

/*
 * Copyright 2016-present Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may obtain
 * a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package com.facebook.buck.parser;

import com.facebook.buck.model.BuildTargetException;
import com.facebook.buck.rules.Cell;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;

import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

class PipelineNodeCache<K, T> {
    private final Cache<K, T> cache;
    protected final ConcurrentMap<K, ListenableFuture<T>> jobsCache;

    public PipelineNodeCache(Cache<K, T> cache) {
        this.jobsCache = new ConcurrentHashMap<>();
        this.cache = cache;
    }

    /**
     * Helper for de-duping jobs against the cache.
     *
     * @param jobSupplier a supplier to use to create the actual job.
     * @return future describing the job. It can either be an immediate future (result cache hit),
     *         ongoing job (job cache hit) or a new job (miss).
     */
    protected final ListenableFuture<T> getJobWithCacheLookup(final Cell cell, final K key,
            JobSupplier<T> jobSupplier) throws BuildTargetException {
        Optional<T> cacheLookupResult = cache.lookupComputedNode(cell, key);
        if (cacheLookupResult.isPresent()) {
            return Futures.immediateFuture(cacheLookupResult.get());
        }

        ListenableFuture<T> speculativeCacheLookupResult = jobsCache.get(key);
        if (speculativeCacheLookupResult != null) {
            return speculativeCacheLookupResult;
        }

        // We use a SettableFuture to resolve any races between threads that are trying to create the
        // job for the given key. The SettableFuture is cheap to throw away in case we didn't "win" and
        // can be easily "connected" to a future that actually does work in case we did.
        SettableFuture<T> resultFutureCandidate = SettableFuture.create();
        ListenableFuture<T> resultFutureInCache = jobsCache.putIfAbsent(key, resultFutureCandidate);
        if (resultFutureInCache != null) {
            // Another thread succeeded in putting the new value into the cache.
            return resultFutureInCache;
        }
        // Ok, "our" candidate future went into the jobsCache, schedule the job and 'chain' the result
        // to the SettableFuture, so that anyone else waiting on it will get the same result.
        final SettableFuture<T> resultFuture = resultFutureCandidate;
        try {
            ListenableFuture<T> nodeJob = Futures.transformAsync(jobSupplier.get(),
                    input -> Futures.immediateFuture(cache.putComputedNodeIfNotPresent(cell, key, input)));
            resultFuture.setFuture(nodeJob);
        } catch (Throwable t) {
            resultFuture.setException(t);
            throw t;
        }
        return resultFuture;
    }

    protected interface JobSupplier<V> {
        ListenableFuture<V> get() throws BuildTargetException;
    }

    public interface Cache<K, V> {
        Optional<V> lookupComputedNode(Cell cell, K target) throws BuildTargetException;

        /**
         * Insert item into the cache if it was not already there.
         * @param cell cell
         * @param target target of the node
         * @param targetNode node to insert
         * @return previous node for the target if the cache contained it, new one otherwise.
         */
        V putComputedNodeIfNotPresent(Cell cell, K target, V targetNode) throws BuildTargetException;
    }
}