Reduce needless contention in the include scanner's fileParseCache. The
computeIfAbsent() function of ConcurrentHashMap is supposed to have a fast
implementation and the map will then allow ~number of processors concurrent
writes. However, reading a file and extracting the include lines can be a costly
IO operation that does not fit this bill. Use futures instead.
RELNOTES: None.
PiperOrigin-RevId: 344799253
diff --git a/src/main/java/com/google/devtools/build/lib/includescanning/LegacyIncludeScanner.java b/src/main/java/com/google/devtools/build/lib/includescanning/LegacyIncludeScanner.java
index 5dd4f42..9a689db 100644
--- a/src/main/java/com/google/devtools/build/lib/includescanning/LegacyIncludeScanner.java
+++ b/src/main/java/com/google/devtools/build/lib/includescanning/LegacyIncludeScanner.java
@@ -15,7 +15,6 @@
import com.google.common.base.Preconditions;
import com.google.common.base.Supplier;
-import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
@@ -58,6 +57,7 @@
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
/**
* C include scanner. Quickly scans C/C++ source files to determine the bounding set of transitively
@@ -858,42 +858,37 @@
checkForInterrupt("processing", source);
Collection<Inclusion> inclusions;
- try {
- inclusions =
- fileParseCache
- .computeIfAbsent(
- source,
- file -> {
- try {
- return Futures.immediateFuture(
- parser.extractInclusions(
- file,
- actionExecutionMetadata,
- actionExecutionContext,
- grepIncludes,
- spawnIncludeScannerSupplier.get(),
- isRealOutputFile(source.getExecPath())));
- } catch (IOException e) {
- throw new IORuntimeException(e);
- } catch (ExecException e) {
- throw new ExecRuntimeException(e);
- } catch (InterruptedException e) {
- throw new InterruptedRuntimeException(e);
- }
- })
- .get();
- } catch (ExecutionException ee) {
+ SettableFuture<Collection<Inclusion>> future = SettableFuture.create();
+ Future<Collection<Inclusion>> previous = fileParseCache.putIfAbsent(source, future);
+ if (previous == null) {
+ previous = future;
try {
- Throwables.throwIfInstanceOf(ee.getCause(), RuntimeException.class);
- throw new IllegalStateException(ee.getCause());
- } catch (IORuntimeException e) {
- throw e.getCauseIOException();
- } catch (ExecRuntimeException e) {
- throw e.getRealCause();
- } catch (InterruptedRuntimeException e) {
- throw e.getRealCause();
+ future.set(
+ parser.extractInclusions(
+ source,
+ actionExecutionMetadata,
+ actionExecutionContext,
+ grepIncludes,
+ spawnIncludeScannerSupplier.get(),
+ isRealOutputFile(source.getExecPath())));
+ } catch (IOException | ExecException | InterruptedException e) {
+ future.setException(e);
+ fileParseCache.remove(source);
+ throw e;
}
}
+ try {
+ inclusions = previous.get();
+ } catch (ExecutionException e) {
+ if (e.getCause() instanceof InterruptedException) {
+ throw (InterruptedException) e.getCause();
+ } else if (e.getCause() instanceof ExecException) {
+ throw (ExecException) e.getCause();
+ } else if (e.getCause() instanceof IOException) {
+ throw (IOException) e.getCause();
+ }
+ throw new IllegalStateException(e.getCause());
+ }
Preconditions.checkNotNull(inclusions, source);
// Shuffle the inclusions to get better parallelism. See b/62200470.