| // Copyright 2023 The Bazel Authors. All rights reserved. | |
| // | |
| // 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.google.devtools.build.lib.concurrent; | |
| import com.google.common.collect.Interner; | |
| import com.google.errorprone.annotations.CanIgnoreReturnValue; | |
| import com.google.errorprone.annotations.ForOverride; | |
| import java.lang.reflect.Field; | |
| import java.util.Map; | |
| import javax.annotation.Nullable; | |
| /** | |
| * An extension of the weak interner which uses a global pool in addition to the weak interner's | |
| * {@code ConcurrentHashMap} to store instances. | |
| * | |
| * <p>The reason of implementing {@link PooledInterner} is that the same object can be stored in | |
| * both weak interner and some other container ({@code InMemoryGraphImple#nodeMap}) in blaze with | |
| * two equal references, causing some memory overhead. | |
| * | |
| * <p>{@link PooledInterner} enables the client to manage where a single object is stored, | |
| * addressing the memory overhead issue. In more detail, | |
| * | |
| * <ul> | |
| * <li>If the object is already canonicalized in the global pool, it should not be stored in | |
| * {@link #weakInterner} again, thus removing the storage overhead of using a traditional weak | |
| * interner; | |
| * <li>User can also remove the object from {@link #weakInterner}'s underlying {@link | |
| * #internerAsMap} when the object appears in the global pool. | |
| * </ul> | |
| * | |
| * <p>Subclasses are only responsible for providing the appropriate {@link Pool} by overriding | |
| * {@link #getPool()} method. | |
| */ | |
| public abstract class PooledInterner<T> implements Interner<T> { | |
| private final Interner<T> weakInterner = BlazeInterners.newWeakInterner(); | |
| private final Map<?, ?> internerAsMap = getMapReflectively(weakInterner); | |
| protected PooledInterner() {} | |
| // There was a Guava API review to include the feature of removing from an interner, and the | |
| // outcome was that we should just get the map reflectively. | |
| private static Map<?, ?> getMapReflectively(Interner<?> interner) { | |
| try { | |
| Field field = interner.getClass().getDeclaredField("map"); | |
| field.setAccessible(true); | |
| return (Map<?, ?>) field.get(interner); | |
| } catch (ReflectiveOperationException e) { | |
| throw new IllegalStateException(e); | |
| } | |
| } | |
| /** | |
| * Interns {@code sample} directly into {@link #weakInterner} without checking the global pool and | |
| * returns the canonical instance of {@code sample}. | |
| */ | |
| @CanIgnoreReturnValue | |
| public final T weakIntern(T sample) { | |
| return weakInterner.intern(sample); | |
| } | |
| /** | |
| * Removes sample from the weak interner. Client can call this method when the sample is already | |
| * stored in the global pool in order to reduce the memory overhead. | |
| */ | |
| public final void removeWeak(Object sample) { | |
| internerAsMap.remove(sample); | |
| } | |
| /** | |
| * Returns the canonical instance of {@code sample} from either global pool or {@link | |
| * #weakInterner}. | |
| */ | |
| @Override | |
| public final T intern(T sample) { | |
| Pool<T> pool = getPool(); | |
| return pool != null ? pool.getOrWeakIntern(sample) : weakInterner.intern(sample); | |
| } | |
| /** | |
| * Provides the global pool instance for {@link #intern(Object)} method. | |
| */ | |
| @ForOverride | |
| @Nullable | |
| protected abstract Pool<T> getPool(); | |
| /** | |
| * An alternative container to the weak interner for storing type T instance. | |
| * | |
| * <p>A pool is a storage space that already exists during normal program execution and provides | |
| * lookup functionality for interning, thus eliminating storage overhead from using a classic weak | |
| * interner. | |
| */ | |
| public interface Pool<T> { | |
| /** | |
| * Returns the canonical instance for the given key in the pool if it is present, otherwise | |
| * interns the key using its {@linkplain #weakIntern weak interner}. | |
| * | |
| * <p>To ensure a single canonical instance, if the key is not present in the pool, it should be | |
| * weakly interned using synchronization so that it is not concurrently {@linkplain #removeWeak | |
| * removed from the weak interner}. | |
| */ | |
| T getOrWeakIntern(T sample); | |
| } | |
| } |