diff options
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/actions/cache/PersistentStringIndexer.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/actions/cache/PersistentStringIndexer.java | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/actions/cache/PersistentStringIndexer.java b/src/main/java/com/google/devtools/build/lib/actions/cache/PersistentStringIndexer.java new file mode 100644 index 0000000000..bd98b2bfda --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/actions/cache/PersistentStringIndexer.java @@ -0,0 +1,161 @@ +// Copyright 2014 Google Inc. 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.actions.cache; + +import com.google.common.collect.MapMaker; +import com.google.devtools.build.lib.concurrent.ThreadSafety.ConditionallyThreadSafe; +import com.google.devtools.build.lib.util.CanonicalStringIndexer; +import com.google.devtools.build.lib.util.Clock; +import com.google.devtools.build.lib.util.PersistentMap; +import com.google.devtools.build.lib.util.StringCanonicalizer; +import com.google.devtools.build.lib.vfs.FileSystemUtils; +import com.google.devtools.build.lib.vfs.Path; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ConcurrentMap; + +/** + * Persistent version of the CanonicalStringIndexer. + * + * <p>This class is backed by a PersistentMap that holds one direction of the + * canonicalization mapping. The other direction is handled purely in memory + * and reconstituted at load-time. + * + * <p>Thread-safety is ensured by locking on all mutating operations from the + * superclass. Read-only operations are not locked, but rather backed by + * ConcurrentMaps. + */ +@ConditionallyThreadSafe // condition: each instance must instantiated with + // different dataFile. +final class PersistentStringIndexer extends CanonicalStringIndexer { + + /** + * Persistent metadata map. Used as a backing map to provide a persistent + * implementation of the metadata cache. + */ + private static final class PersistentIndexMap extends PersistentMap<String, Integer> { + private static final int VERSION = 0x01; + private static final long SAVE_INTERVAL_NS = 3L * 1000 * 1000 * 1000; + + private final Clock clock; + private long nextUpdate; + + public PersistentIndexMap(Path mapFile, Path journalFile, Clock clock) throws IOException { + super(VERSION, PersistentStringIndexer.<String, Integer>newConcurrentMap(INITIAL_ENTRIES), + mapFile, journalFile); + this.clock = clock; + nextUpdate = clock.nanoTime(); + load(/*throwOnLoadFailure=*/true); + } + + @Override + protected boolean updateJournal() { + long time = clock.nanoTime(); + if (SAVE_INTERVAL_NS == 0 || time > nextUpdate) { + nextUpdate = time + SAVE_INTERVAL_NS; + return true; + } + return false; + } + + @Override + public Integer remove(Object object) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + public void flush() { + super.forceFlush(); + } + + @Override + protected String readKey(DataInputStream in) throws IOException { + int length = in.readInt(); + if (length < 0) { + throw new IOException("corrupt key length: " + length); + } + byte[] content = new byte[length]; + in.readFully(content); + return StringCanonicalizer.intern(bytes2string(content)); + } + + @Override + protected Integer readValue(DataInputStream in) throws IOException { + return in.readInt(); + } + + @Override + protected void writeKey(String key, DataOutputStream out) throws IOException { + byte[] content = string2bytes(key); + out.writeInt(content.length); + out.write(content); + } + + @Override + protected void writeValue(Integer value, DataOutputStream out) throws IOException { + out.writeInt(value); + } + } + + private final PersistentIndexMap persistentIndexMap; + private static final int INITIAL_ENTRIES = 10000; + + /** + * Instantiates and loads instance of the persistent string indexer. + */ + static PersistentStringIndexer newPersistentStringIndexer(Path dataPath, + Clock clock) throws IOException { + PersistentIndexMap persistentIndexMap = new PersistentIndexMap(dataPath, + FileSystemUtils.replaceExtension(dataPath, ".journal"), clock); + Map<Integer, String> reverseMapping = newConcurrentMap(INITIAL_ENTRIES); + for (Map.Entry<String, Integer> entry : persistentIndexMap.entrySet()) { + if (reverseMapping.put(entry.getValue(), entry.getKey()) != null) { + throw new IOException("Corrupted filename index has duplicate entry: " + entry.getKey()); + } + } + return new PersistentStringIndexer(persistentIndexMap, reverseMapping); + } + + private PersistentStringIndexer(PersistentIndexMap stringToInt, + Map<Integer, String> intToString) { + super(stringToInt, intToString); + this.persistentIndexMap = stringToInt; + } + + /** + * Saves index data to the file. + */ + synchronized long save() throws IOException { + return persistentIndexMap.save(); + } + + /** + * Flushes the journal. + */ + synchronized void flush() { + persistentIndexMap.flush(); + } + + private static <K, V> ConcurrentMap<K, V> newConcurrentMap(int expectedCapacity) { + return new MapMaker().initialCapacity(expectedCapacity).makeMap(); + } + +} |