All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.gradle.cache.internal.ProducerGuard Maven / Gradle / Ivy

There is a newer version: 8.11.1
Show newest version
/*
 * Copyright 2017 the original author or authors.
 *
 * 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 org.gradle.cache.internal;

import com.google.common.collect.Sets;
import com.google.common.util.concurrent.Striped;
import org.gradle.internal.Factory;
import org.gradle.internal.UncheckedException;

import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Synchronizes access to some resource, by making sure that 2 threads do not try to produce it at the same time.
 * The resource to be accessed is represented by a key, and the factory is whatever needs to be done to produce it.
 * This is not a cache. The factory should take care of caching wherever it makes sense.
 *
 * 

* The concurrency level and memory usage depend on the implementation, see {@link #adaptive()}, {@link #striped()} and {@link #serial()} for details. *

* * @param the type of key to lock on */ public abstract class ProducerGuard { /** * Creates a {@link ProducerGuard} which guarantees that different keys will block on different locks, * ensuring maximum concurrency. This comes at the cost of allocating locks for each key, leading to * relatively high memory pressure. If the above guarantee is not necessary, consider using a {@link #striped()} * guard instead. */ public static ProducerGuard adaptive() { return new AdaptiveProducerGuard(); } /** * Creates a {@link ProducerGuard} which evenly spreads calls over a fixed number of locks. * This means that in some cases two different keys can block on the same lock. The benefit of * this strategy is that it uses only a fixed amount of memory. If your code depends on * different keys always getting different locks, use a {@link #adaptive()} guard instead. */ public static ProducerGuard striped() { return new StripedProducerGuard(); } /** * Creates a {@link ProducerGuard} which limits concurrency to a single factory at a time, * ignoring the key. This is mainly useful for testing. */ public static ProducerGuard serial() { return new SerialProducerGuard(); } private ProducerGuard() { } /** * Runs the given factory, guarded by the given key. * * @param key the key to lock on * @param factory the factory to run under the lock * @param the type returned by the factory * @return the value returned by the factory */ public abstract V guardByKey(T key, Factory factory); private static class AdaptiveProducerGuard extends ProducerGuard { private final Set producing = Sets.newHashSet(); @Override public V guardByKey(T key, Factory factory) { synchronized (producing) { while (!producing.add(key)) { try { producing.wait(); } catch (InterruptedException e) { throw UncheckedException.throwAsUncheckedException(e); } } } try { return factory.create(); } finally { synchronized (producing) { producing.remove(key); producing.notifyAll(); } } } } private static class StripedProducerGuard extends ProducerGuard { private final Striped locks = Striped.lock(Runtime.getRuntime().availableProcessors() * 4); @Override public V guardByKey(T key, Factory factory) { Lock lock = locks.get(key); try { lock.lock(); return factory.create(); } finally { lock.unlock(); } } } private static class SerialProducerGuard extends ProducerGuard { private final Lock lock = new ReentrantLock(); @Override public V guardByKey(T key, Factory factory) { try { lock.lock(); return factory.create(); } finally { lock.unlock(); } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy