Clean up FilesystemCalls: rename to SyscallCache, move to a top-level class, and get rid of AtomicReference wherever possible. Use a SyscallCache for any command-scoped classes, and Supplier<SyscallCache> for others. This change should have no runtime-observable effects (all underlying caches are the same).

Also clean up AtomicReference<TimestampGranularityMonitor> in the same way, since the parameters are passed together, and set it to non-null wherever possible.

PiperOrigin-RevId: 424687480
diff --git a/src/main/java/com/google/devtools/build/lib/actions/ActionExecutionContext.java b/src/main/java/com/google/devtools/build/lib/actions/ActionExecutionContext.java
index c155dc9..9230394 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/ActionExecutionContext.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/ActionExecutionContext.java
@@ -31,7 +31,7 @@
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.Root;
-import com.google.devtools.build.lib.vfs.UnixGlob.FilesystemCalls;
+import com.google.devtools.build.lib.vfs.SyscallCache;
 import com.google.devtools.build.skyframe.SkyFunction.Environment;
 import com.google.devtools.common.options.OptionsProvider;
 import java.io.Closeable;
@@ -76,7 +76,7 @@
 
   private final ArtifactPathResolver pathResolver;
   private final DiscoveredModulesPruner discoveredModulesPruner;
-  private final FilesystemCalls syscalls;
+  private final SyscallCache syscallCache;
   private final ThreadStateReceiver threadStateReceiverForMetrics;
 
   private ActionExecutionContext(
@@ -96,7 +96,7 @@
       @Nullable FileSystem actionFileSystem,
       @Nullable Object skyframeDepsResult,
       DiscoveredModulesPruner discoveredModulesPruner,
-      FilesystemCalls syscalls,
+      SyscallCache syscallCache,
       ThreadStateReceiver threadStateReceiverForMetrics) {
     this.actionInputFileCache = actionInputFileCache;
     this.actionInputPrefetcher = actionInputPrefetcher;
@@ -118,7 +118,7 @@
         // executor is only ever null in testing.
         executor == null ? null : executor.getExecRoot());
     this.discoveredModulesPruner = discoveredModulesPruner;
-    this.syscalls = syscalls;
+    this.syscallCache = syscallCache;
   }
 
   public ActionExecutionContext(
@@ -137,7 +137,7 @@
       @Nullable FileSystem actionFileSystem,
       @Nullable Object skyframeDepsResult,
       DiscoveredModulesPruner discoveredModulesPruner,
-      FilesystemCalls syscalls,
+      SyscallCache syscallCache,
       ThreadStateReceiver threadStateReceiverForMetrics) {
     this(
         executor,
@@ -156,7 +156,7 @@
         actionFileSystem,
         skyframeDepsResult,
         discoveredModulesPruner,
-        syscalls,
+        syscallCache,
         threadStateReceiverForMetrics);
   }
 
@@ -174,7 +174,7 @@
       Environment env,
       @Nullable FileSystem actionFileSystem,
       DiscoveredModulesPruner discoveredModulesPruner,
-      FilesystemCalls syscalls,
+      SyscallCache syscalls,
       ThreadStateReceiver threadStateReceiverForMetrics) {
     return new ActionExecutionContext(
         executor,
@@ -382,12 +382,12 @@
   }
 
   /**
-   * This only exists for loose header checking (and shouldn't be exist at all).
+   * This only exists for loose header checking (and shouldn't exist at all).
    *
    * <p>Do NOT use from any other place.
    */
-  public FilesystemCalls getSyscalls() {
-    return syscalls;
+  public SyscallCache getSyscallCache() {
+    return syscallCache;
   }
 
   public ThreadStateReceiver getThreadStateReceiverForMetrics() {
@@ -428,7 +428,7 @@
         actionFileSystem,
         skyframeDepsResult,
         discoveredModulesPruner,
-        syscalls,
+        syscallCache,
         threadStateReceiverForMetrics);
   }
 
@@ -451,7 +451,7 @@
         actionFileSystem,
         skyframeDepsResult,
         discoveredModulesPruner,
-        syscalls,
+        syscallCache,
         threadStateReceiverForMetrics);
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/actions/FileStateValue.java b/src/main/java/com/google/devtools/build/lib/actions/FileStateValue.java
index 8ce0bbc..c3289d5 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/FileStateValue.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/FileStateValue.java
@@ -34,7 +34,7 @@
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.lib.vfs.RootedPath;
 import com.google.devtools.build.lib.vfs.Symlinks;
-import com.google.devtools.build.lib.vfs.UnixGlob.FilesystemCalls;
+import com.google.devtools.build.lib.vfs.SyscallCache;
 import com.google.devtools.build.skyframe.AbstractSkyKey;
 import com.google.devtools.build.skyframe.SkyFunctionName;
 import com.google.devtools.build.skyframe.SkyValue;
@@ -76,9 +76,7 @@
   private FileStateValue() {}
 
   public static FileStateValue create(
-      RootedPath rootedPath,
-      FilesystemCalls syscallCache,
-      @Nullable TimestampGranularityMonitor tsgm)
+      RootedPath rootedPath, SyscallCache syscallCache, @Nullable TimestampGranularityMonitor tsgm)
       throws IOException {
     Path path = rootedPath.asPath();
     Dirent.Type type = syscallCache.getType(path, Symlinks.NOFOLLOW);
diff --git a/src/main/java/com/google/devtools/build/lib/includescanning/IncludeParser.java b/src/main/java/com/google/devtools/build/lib/includescanning/IncludeParser.java
index ab119a4..fdf1621 100644
--- a/src/main/java/com/google/devtools/build/lib/includescanning/IncludeParser.java
+++ b/src/main/java/com/google/devtools/build/lib/includescanning/IncludeParser.java
@@ -52,8 +52,8 @@
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.lib.vfs.Root;
+import com.google.devtools.build.lib.vfs.SyscallCache;
 import com.google.devtools.build.lib.vfs.UnixGlob;
-import com.google.devtools.build.lib.vfs.UnixGlob.FilesystemCalls;
 import com.google.devtools.build.skyframe.SkyFunction.Environment;
 import com.google.devtools.build.skyframe.SkyKey;
 import com.google.devtools.build.skyframe.SkyValue;
@@ -67,7 +67,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.atomic.AtomicReference;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.regex.PatternSyntaxException;
@@ -77,6 +76,9 @@
  * Scans a source file and extracts the literal inclusions it specifies. Does not store results --
  * repeated requests to the same file will result in repeated scans. Clients should implement a
  * caching layer in order to avoid unnecessary disk access when requesting an already scanned file.
+ *
+ * <p>Both this class and the static inner class {@link Hints} have lifetime of a single build (or a
+ * single include scanning operation in the case of the {@link SwigIncludeParser}).
  */
 @VisibleForTesting
 class IncludeParser {
@@ -188,7 +190,8 @@
     private final ImmutableList<Rule> rules;
     private final ArtifactFactory artifactFactory;
 
-    private final AtomicReference<FilesystemCalls> syscallCache = new AtomicReference<>();
+    private final SyscallCache syscallCache =
+        PerBuildSyscallCache.newBuilder().setInitialCapacity(HINTS_CACHE_CONCURRENCY).build();
 
     private final LoadingCache<Artifact, ImmutableList<Artifact>> fileLevelHintsCache =
         Caffeine.newBuilder()
@@ -203,7 +206,6 @@
     Hints(HintsRules hintsRules, ArtifactFactory artifactFactory) {
       this.artifactFactory = artifactFactory;
       this.rules = hintsRules.rules;
-      clearCachedLegacyHints();
     }
 
     static HintsRules getRules(Path hintsFile) throws IOException {
@@ -242,16 +244,6 @@
       return new HintsRules(rules.build());
     }
 
-    /**
-     * Clears legacy inclusions cache to maintain inter-build correctness, since filesystem changes
-     * are not tracked by cache.
-     */
-    void clearCachedLegacyHints() {
-      fileLevelHintsCache.invalidateAll();
-      syscallCache.set(
-          PerBuildSyscallCache.newBuilder().setInitialCapacity(HINTS_CACHE_CONCURRENCY).build());
-    }
-
     /** Returns the "file" type hinted inclusions for a given path, caching results by path. */
     ImmutableList<Artifact> getFileLevelHintedInclusionsLegacy(Artifact path) {
       if (!path.getExecPathString().startsWith(ALLOWED_PREFIX)) {
diff --git a/src/main/java/com/google/devtools/build/lib/includescanning/IncludeScannerSupplier.java b/src/main/java/com/google/devtools/build/lib/includescanning/IncludeScannerSupplier.java
index ee4bc79..c521146 100644
--- a/src/main/java/com/google/devtools/build/lib/includescanning/IncludeScannerSupplier.java
+++ b/src/main/java/com/google/devtools/build/lib/includescanning/IncludeScannerSupplier.java
@@ -131,10 +131,5 @@
     Preconditions.checkState(includeParseCache.isEmpty(), includeParseCache);
     Preconditions.checkState(scanners.asMap().isEmpty(), scanners);
     this.includeParser = Preconditions.checkNotNull(includeParser);
-    if (this.includeParser.getHints() != null) {
-      // The Hints object lives across the lifetime of the Blaze server, but its cached hints may
-      // be stale.
-      this.includeParser.getHints().clearCachedLegacyHints();
-    }
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/includescanning/IncludeScanningModule.java b/src/main/java/com/google/devtools/build/lib/includescanning/IncludeScanningModule.java
index 9bd17e3..4352d0c 100644
--- a/src/main/java/com/google/devtools/build/lib/includescanning/IncludeScanningModule.java
+++ b/src/main/java/com/google/devtools/build/lib/includescanning/IncludeScanningModule.java
@@ -254,7 +254,7 @@
           new SpawnIncludeScanner(
               env.getExecRoot(),
               options.experimentalRemoteExtractionThreshold,
-              env.getSkyframeExecutor().getSyscalls()));
+              env.getSyscallCache()));
       this.spawnScannerSupplier = spawnScannerSupplier;
       env.getEventBus().register(this);
     }
diff --git a/src/main/java/com/google/devtools/build/lib/includescanning/SpawnIncludeScanner.java b/src/main/java/com/google/devtools/build/lib/includescanning/SpawnIncludeScanner.java
index 3aa231b..edd1261 100644
--- a/src/main/java/com/google/devtools/build/lib/includescanning/SpawnIncludeScanner.java
+++ b/src/main/java/com/google/devtools/build/lib/includescanning/SpawnIncludeScanner.java
@@ -51,17 +51,16 @@
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.lib.vfs.Symlinks;
-import com.google.devtools.build.lib.vfs.UnixGlob.FilesystemCalls;
+import com.google.devtools.build.lib.vfs.SyscallCache;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Collection;
 import java.util.List;
-import java.util.concurrent.atomic.AtomicReference;
 import javax.annotation.Nullable;
 
 /**
  * C include scanner. Scans C/C++ source files using spawns to determine the bounding set of
- * transitively referenced include files.
+ * transitively referenced include files. Has lifetime of a single build.
  */
 public class SpawnIncludeScanner {
   /** The grep-includes tool is very lightweight, so don't use the default from AbstractAction. */
@@ -72,11 +71,11 @@
   private OutputService outputService;
   private boolean inMemoryOutput;
   private final int remoteExtractionThreshold;
-  private final AtomicReference<FilesystemCalls> syscallCache;
+  private final SyscallCache syscallCache;
 
   /** Constructs a new SpawnIncludeScanner. */
   public SpawnIncludeScanner(
-      Path execRoot, int remoteExtractionThreshold, AtomicReference<FilesystemCalls> syscallCache) {
+      Path execRoot, int remoteExtractionThreshold, SyscallCache syscallCache) {
     this.execRoot = execRoot;
     this.remoteExtractionThreshold = remoteExtractionThreshold;
     this.syscallCache = syscallCache;
@@ -126,7 +125,7 @@
     if (remoteExtractionThreshold == 0 || (outputService != null && !file.isSourceArtifact())) {
       return true;
     }
-    FileStatus status = syscallCache.get().statIfFound(file.getPath(), Symlinks.FOLLOW);
+    FileStatus status = syscallCache.statIfFound(file.getPath(), Symlinks.FOLLOW);
     return status == null || status.getSize() > remoteExtractionThreshold;
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/packages/GlobCache.java b/src/main/java/com/google/devtools/build/lib/packages/GlobCache.java
index 11caecc..818eef4 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/GlobCache.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/GlobCache.java
@@ -27,6 +27,7 @@
 import com.google.devtools.build.lib.util.Pair;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.SyscallCache;
 import com.google.devtools.build.lib.vfs.UnixGlob;
 import com.google.devtools.build.lib.vfs.UnixGlobPathDiscriminator;
 import java.io.IOException;
@@ -42,9 +43,8 @@
 import java.util.concurrent.Executor;
 import java.util.concurrent.Future;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
 
-/** Caches the results of glob expansion for a package. */
+/** Caches the results of glob expansion for a package. Has lifetime of a single build. */
 @ThreadSafety.ThreadCompatible
 public class GlobCache {
   /**
@@ -65,7 +65,7 @@
   private final PackageIdentifier packageId;
 
   /** System call caching layer. */
-  private final AtomicReference<? extends UnixGlob.FilesystemCalls> syscalls;
+  private final SyscallCache syscallCache;
 
   private final int maxDirectoriesToEagerlyVisit;
 
@@ -95,7 +95,7 @@
       final PackageIdentifier packageId,
       final ImmutableSet<PathFragment> ignoredGlobPrefixes,
       final CachingPackageLocator locator,
-      AtomicReference<? extends UnixGlob.FilesystemCalls> syscalls,
+      SyscallCache syscallCache,
       Executor globExecutor,
       int maxDirectoriesToEagerlyVisit,
       ThreadStateReceiver threadStateReceiverForMetrics) {
@@ -110,7 +110,7 @@
                     command.run();
                   }
                 });
-    this.syscalls = syscalls == null ? new AtomicReference<>(UnixGlob.DEFAULT_SYSCALLS) : syscalls;
+    this.syscallCache = syscallCache;
     this.maxDirectoriesToEagerlyVisit = maxDirectoriesToEagerlyVisit;
 
     Preconditions.checkNotNull(locator);
@@ -223,7 +223,7 @@
           .addPattern(pattern)
           .setPathDiscriminator(new GlobUnixPathDiscriminator(globberOperation))
           .setExecutor(globExecutor)
-          .setFilesystemCalls(syscalls)
+          .setFilesystemCalls(syscallCache)
           .globAsync();
     } catch (UnixGlob.BadPattern ex) {
       throw new BadGlobException(ex.getMessage());
diff --git a/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java b/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
index 9cd3424..e07a3ef 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
@@ -46,15 +46,15 @@
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.lib.vfs.RootedPath;
-import com.google.devtools.build.lib.vfs.UnixGlob;
+import com.google.devtools.build.lib.vfs.SyscallCache;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.OptionalLong;
 import java.util.Set;
 import java.util.concurrent.ForkJoinPool;
-import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Consumer;
+import java.util.function.Supplier;
 import net.starlark.java.eval.Dict;
 import net.starlark.java.eval.EvalException;
 import net.starlark.java.eval.Module;
@@ -114,7 +114,7 @@
   private final RuleFactory ruleFactory;
   private final RuleClassProvider ruleClassProvider;
 
-  private AtomicReference<? extends UnixGlob.FilesystemCalls> syscalls;
+  private Supplier<? extends SyscallCache> syscalls;
 
   private ForkJoinPool executor;
 
@@ -211,7 +211,7 @@
   }
 
   /** Sets the syscalls cache used in globbing. */
-  public void setSyscalls(AtomicReference<? extends UnixGlob.FilesystemCalls> syscalls) {
+  public void setSyscallCache(Supplier<? extends SyscallCache> syscalls) {
     this.syscalls = Preconditions.checkNotNull(syscalls);
   }
 
@@ -481,7 +481,7 @@
             packageId,
             ignoredGlobPrefixes,
             locator,
-            syscalls,
+            syscalls.get(),
             executor,
             maxDirectoriesToEagerlyVisitInGlobbing,
             threadStateReceiverForMetrics));
diff --git a/src/main/java/com/google/devtools/build/lib/pkgcache/PathPackageLocator.java b/src/main/java/com/google/devtools/build/lib/pkgcache/PathPackageLocator.java
index c5c4be5..e7fda08 100644
--- a/src/main/java/com/google/devtools/build/lib/pkgcache/PathPackageLocator.java
+++ b/src/main/java/com/google/devtools/build/lib/pkgcache/PathPackageLocator.java
@@ -32,12 +32,11 @@
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.lib.vfs.Root;
 import com.google.devtools.build.lib.vfs.Symlinks;
-import com.google.devtools.build.lib.vfs.UnixGlob;
+import com.google.devtools.build.lib.vfs.SyscallCache;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
-import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * A mapping from the name of a package to the location of its BUILD file. The implementation
@@ -77,7 +76,7 @@
    * first path that matches always wins.
    */
   public Path getPackageBuildFile(PackageIdentifier packageName) throws NoSuchPackageException {
-    Path buildFile  = getPackageBuildFileNullable(packageName, UnixGlob.DEFAULT_SYSCALLS_REF);
+    Path buildFile = getPackageBuildFileNullable(packageName, SyscallCache.NO_CACHE);
     if (buildFile == null) {
       String message = "BUILD file not found on package path";
       throw new BuildFileNotFoundException(
@@ -102,9 +101,7 @@
    * @param cache a filesystem-level cache of stat() calls.
    * @return the {@link Path} to the correct build file, or {@code null} if none was found
    */
-  public Path getPackageBuildFileNullable(
-      PackageIdentifier packageIdentifier,
-      AtomicReference<? extends UnixGlob.FilesystemCalls> cache) {
+  public Path getPackageBuildFileNullable(PackageIdentifier packageIdentifier, SyscallCache cache) {
     if (packageIdentifier.getRepository().isMain()) {
       for (BuildFileName buildFileName : buildFilesByPriority) {
         Path buildFilePath =
@@ -133,7 +130,7 @@
                 .getRelative(packageIdentifier.getSourceRoot())
                 .getRelative(buildFileName.getFilenameFragment());
         try {
-          FileStatus stat = cache.get().statIfFound(buildFile, Symlinks.FOLLOW);
+          FileStatus stat = cache.statIfFound(buildFile, Symlinks.FOLLOW);
           if (stat != null && stat.isFile()) {
             return buildFile;
           }
@@ -257,22 +254,21 @@
    * wins.
    */
   public Path getWorkspaceFile() {
-    AtomicReference<? extends UnixGlob.FilesystemCalls> cache = UnixGlob.DEFAULT_SYSCALLS_REF;
     // TODO(bazel-team): correctness in the presence of changes to the location of the WORKSPACE
     // file.
-    Path workspaceFile = getFilePath(LabelConstants.WORKSPACE_DOT_BAZEL_FILE_NAME, cache);
+    Path workspaceFile =
+        getFilePath(LabelConstants.WORKSPACE_DOT_BAZEL_FILE_NAME, SyscallCache.NO_CACHE);
     if (workspaceFile != null) {
       return workspaceFile;
     }
-    return getFilePath(LabelConstants.WORKSPACE_FILE_NAME, cache);
+    return getFilePath(LabelConstants.WORKSPACE_FILE_NAME, SyscallCache.NO_CACHE);
   }
 
-  private Path getFilePath(PathFragment suffix,
-      AtomicReference<? extends UnixGlob.FilesystemCalls> cache) {
+  private Path getFilePath(PathFragment suffix, SyscallCache cache) {
     for (Root pathEntry : pathEntries) {
       Path buildFile = pathEntry.getRelative(suffix);
       try {
-        Dirent.Type type = cache.get().getType(buildFile, Symlinks.FOLLOW);
+        Dirent.Type type = cache.getType(buildFile, Symlinks.FOLLOW);
         if (type == Dirent.Type.FILE || type == Dirent.Type.UNKNOWN) {
           return buildFile;
         }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java
index fc377ee..51d68ef 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java
@@ -1117,7 +1117,7 @@
             // TODO(b/187366935): Consider globbing everything eagerly instead.
             fileStatus =
                 actionExecutionContext
-                    .getSyscalls()
+                    .getSyscallCache()
                     .statIfFound(dirOrPackage.getRelative(BUILD_PATH_FRAGMENT), Symlinks.FOLLOW);
           } catch (IOException e) {
             // Previously, we used Path.exists() to check whether the BUILD file exists. This did
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/CommandEnvironment.java b/src/main/java/com/google/devtools/build/lib/runtime/CommandEnvironment.java
index 80020ca..7d2fce9 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/CommandEnvironment.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/CommandEnvironment.java
@@ -51,6 +51,7 @@
 import com.google.devtools.build.lib.vfs.OutputService;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.SyscallCache;
 import com.google.devtools.common.options.OptionsParsingResult;
 import com.google.devtools.common.options.OptionsProvider;
 import com.google.protobuf.Any;
@@ -98,6 +99,7 @@
   private final PathPackageLocator packageLocator;
   private final Path workingDirectory;
   private final PathFragment relativeWorkingDirectory;
+  private final SyscallCache syscallCache;
   private final Duration waitTime;
   private final long commandStartTime;
   private final ImmutableList<Any> commandExtensions;
@@ -218,6 +220,7 @@
     } else {
       this.packageLocator = null;
     }
+    this.syscallCache = workspace.getSkyframeExecutor().getCurrentSyscallCache();
     workspace.getSkyframeExecutor().setEventBus(eventBus);
 
     ClientOptions clientOptions =
@@ -794,6 +797,10 @@
     }
   }
 
+  public SyscallCache getSyscallCache() {
+    return syscallCache;
+  }
+
   /**
    * Returns the {@linkplain
    * com.google.devtools.build.lib.server.CommandProtos.RunRequest#getCommandExtensions extensions}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionFunction.java
index 22c0c1f..f646fb8 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionFunction.java
@@ -112,7 +112,6 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Consumer;
 import java.util.function.IntFunction;
 import java.util.function.Predicate;
@@ -143,13 +142,13 @@
   private final ActionRewindStrategy actionRewindStrategy = new ActionRewindStrategy();
   private final SkyframeActionExecutor skyframeActionExecutor;
   private final BlazeDirectories directories;
-  private final AtomicReference<TimestampGranularityMonitor> tsgm;
+  private final Supplier<TimestampGranularityMonitor> tsgm;
   private final BugReporter bugReporter;
 
   public ActionExecutionFunction(
       SkyframeActionExecutor skyframeActionExecutor,
       BlazeDirectories directories,
-      AtomicReference<TimestampGranularityMonitor> tsgm,
+      Supplier<TimestampGranularityMonitor> tsgm,
       BugReporter bugReporter) {
     this.skyframeActionExecutor = skyframeActionExecutor;
     this.directories = directories;
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/DirectoryListingStateFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/DirectoryListingStateFunction.java
index 8295cec..5df2ca8 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/DirectoryListingStateFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/DirectoryListingStateFunction.java
@@ -15,12 +15,12 @@
 
 import com.google.devtools.build.lib.skyframe.ExternalFilesHelper.FileType;
 import com.google.devtools.build.lib.vfs.RootedPath;
-import com.google.devtools.build.lib.vfs.UnixGlob.FilesystemCalls;
+import com.google.devtools.build.lib.vfs.SyscallCache;
 import com.google.devtools.build.skyframe.SkyFunction;
 import com.google.devtools.build.skyframe.SkyFunctionException;
 import com.google.devtools.build.skyframe.SkyKey;
 import java.io.IOException;
-import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
 
 /**
  * A {@link SkyFunction} for {@link DirectoryListingStateValue}s.
@@ -37,10 +37,10 @@
    * re-use the results of expensive readdir() operations, that are likely already executed for
    * evaluating globs.
    */
-  private final AtomicReference<FilesystemCalls> syscallCache;
+  private final Supplier<SyscallCache> syscallCache;
 
   public DirectoryListingStateFunction(
-      ExternalFilesHelper externalFilesHelper, AtomicReference<FilesystemCalls> syscallCache) {
+      ExternalFilesHelper externalFilesHelper, Supplier<SyscallCache> syscallCache) {
     this.externalFilesHelper = externalFilesHelper;
     this.syscallCache = syscallCache;
   }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/FileStateFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/FileStateFunction.java
index de7f6d4..41399fc 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/FileStateFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/FileStateFunction.java
@@ -18,12 +18,12 @@
 import com.google.devtools.build.lib.skyframe.ExternalFilesHelper.FileType;
 import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
 import com.google.devtools.build.lib.vfs.RootedPath;
-import com.google.devtools.build.lib.vfs.UnixGlob.FilesystemCalls;
+import com.google.devtools.build.lib.vfs.SyscallCache;
 import com.google.devtools.build.skyframe.SkyFunction;
 import com.google.devtools.build.skyframe.SkyFunctionException;
 import com.google.devtools.build.skyframe.SkyKey;
 import java.io.IOException;
-import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
 
 /**
  * A {@link SkyFunction} for {@link FileStateValue}s.
@@ -33,13 +33,13 @@
  */
 public class FileStateFunction implements SkyFunction {
 
-  private final AtomicReference<TimestampGranularityMonitor> tsgm;
-  private final AtomicReference<FilesystemCalls> syscallCache;
+  private final Supplier<TimestampGranularityMonitor> tsgm;
+  private final Supplier<SyscallCache> syscallCache;
   private final ExternalFilesHelper externalFilesHelper;
 
   public FileStateFunction(
-      AtomicReference<TimestampGranularityMonitor> tsgm,
-      AtomicReference<FilesystemCalls> syscallCache,
+      Supplier<TimestampGranularityMonitor> tsgm,
+      Supplier<SyscallCache> syscallCache,
       ExternalFilesHelper externalFilesHelper) {
     this.tsgm = tsgm;
     this.syscallCache = syscallCache;
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/PerBuildSyscallCache.java b/src/main/java/com/google/devtools/build/lib/skyframe/PerBuildSyscallCache.java
index 00ea4b1..b9e9af8 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/PerBuildSyscallCache.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/PerBuildSyscallCache.java
@@ -22,7 +22,7 @@
 import com.google.devtools.build.lib.vfs.FileStatus;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.Symlinks;
-import com.google.devtools.build.lib.vfs.UnixGlob;
+import com.google.devtools.build.lib.vfs.SyscallCache;
 import java.io.IOException;
 import java.util.Collection;
 
@@ -31,7 +31,7 @@
  *
  * <p>Mostly used by non-Skyframe globbing and include parsing.
  */
-public final class PerBuildSyscallCache implements UnixGlob.FilesystemCalls {
+public final class PerBuildSyscallCache implements SyscallCache {
 
   private final LoadingCache<Pair<Path, Symlinks>, Object> statCache;
 
@@ -139,14 +139,14 @@
         if (result == NO_STATUS) {
           return null;
         }
-        return UnixGlob.statusToDirentType((FileStatus) result);
+        return SyscallCache.statusToDirentType((FileStatus) result);
       }
     }
 
     // If this is a root directory, we must stat, there is no parent.
     Path parent = path.getParentDirectory();
     if (parent == null) {
-      return UnixGlob.statusToDirentType(statIfFound(path, symlinks));
+      return SyscallCache.statusToDirentType(statIfFound(path, symlinks));
     }
 
     // Answer based on a cached readdir() call if possible. The cache might already be populated
@@ -171,14 +171,14 @@
         }
         if (dirent.getType() == Dirent.Type.SYMLINK && symlinks == Symlinks.FOLLOW) {
           // See above: We don't want to follow symlinks with readdir(). Do a stat() instead.
-          return UnixGlob.statusToDirentType(statIfFound(path, Symlinks.FOLLOW));
+          return SyscallCache.statusToDirentType(statIfFound(path, Symlinks.FOLLOW));
         }
         return dirent.getType();
       }
       return null;
     }
 
-    return UnixGlob.statusToDirentType(statIfFound(path, symlinks));
+    return SyscallCache.statusToDirentType(statIfFound(path, symlinks));
   }
 
   public void clear() {
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunction.java
index dd63561..2683512 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunction.java
@@ -13,7 +13,6 @@
 // limitations under the License.
 package com.google.devtools.build.lib.skyframe;
 
-import static com.google.devtools.build.lib.vfs.UnixGlob.DEFAULT_SYSCALLS;
 import static java.nio.charset.StandardCharsets.UTF_8;
 
 import com.google.common.annotations.VisibleForTesting;
@@ -54,6 +53,7 @@
 import com.google.devtools.build.lib.vfs.Root;
 import com.google.devtools.build.lib.vfs.RootedPath;
 import com.google.devtools.build.lib.vfs.Symlinks;
+import com.google.devtools.build.lib.vfs.SyscallCache;
 import com.google.devtools.build.skyframe.SkyFunction;
 import com.google.devtools.build.skyframe.SkyFunctionException;
 import com.google.devtools.build.skyframe.SkyKey;
@@ -339,7 +339,7 @@
         // remove some of the filesystem operations we're doing here.
         Path path = traversal.root.asRootedPath().asPath();
         FileStateValue fileState =
-            FileStateValue.create(traversal.root.asRootedPath(), DEFAULT_SYSCALLS, null);
+            FileStateValue.create(traversal.root.asRootedPath(), SyscallCache.NO_CACHE, null);
         if (fileState.getType() == FileStateType.NONEXISTENT) {
           throw new IOException("Missing file: " + path);
         }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutor.java
index 32a2bcb..787a263 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutor.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutor.java
@@ -771,6 +771,7 @@
       throws InterruptedException, AbruptExitException {
     TimestampGranularityMonitor tsgm = this.tsgm.get();
     Differencer.Diff diff;
+    syscallCacheRef.set(getPerBuildSyscallCache(/*globbingThreads=*/ 42));
     if (modifiedFileSet.treatEverythingAsModified()) {
       diff =
           new FilesystemValueChecker(tsgm, /*lastExecutionTimeRange=*/ null, /*numThreads=*/ 200)
@@ -778,7 +779,6 @@
     } else {
       diff = getDiff(tsgm, modifiedFileSet, pathEntry, /* fsvcThreads= */ 200);
     }
-    syscalls.set(getPerBuildSyscallCache(/*globbingThreads=*/ 42));
     recordingDiffer.invalidate(diff.changedKeysWithoutNewValues());
     recordingDiffer.inject(diff.changedKeysWithNewValues());
     // Blaze invalidates transient errors on every build.
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeActionExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeActionExecutor.java
index a1edb6e..111b866 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeActionExecutor.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeActionExecutor.java
@@ -101,7 +101,7 @@
 import com.google.devtools.build.lib.vfs.OutputService.ActionFileSystemType;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.Root;
-import com.google.devtools.build.lib.vfs.UnixGlob.FilesystemCalls;
+import com.google.devtools.build.lib.vfs.SyscallCache;
 import com.google.devtools.build.skyframe.SkyFunction.Environment;
 import com.google.devtools.build.skyframe.SkyKey;
 import com.google.devtools.common.options.OptionsProvider;
@@ -146,7 +146,7 @@
   private final ActionKeyContext actionKeyContext;
   private final MetadataConsumerForMetrics outputArtifactsSeen;
   private final MetadataConsumerForMetrics outputArtifactsFromActionCache;
-  private final AtomicReference<FilesystemCalls> syscalls;
+  private final Supplier<SyscallCache> syscallCache;
   private final Function<SkyKey, ThreadStateReceiver> threadStateReceiverFactory;
   private Reporter reporter;
   private Map<String, String> clientEnv = ImmutableMap.of();
@@ -218,14 +218,14 @@
       MetadataConsumerForMetrics outputArtifactsFromActionCache,
       AtomicReference<ActionExecutionStatusReporter> statusReporterRef,
       Supplier<ImmutableList<Root>> sourceRootSupplier,
-      AtomicReference<FilesystemCalls> syscalls,
+      Supplier<SyscallCache> syscallCache,
       Function<SkyKey, ThreadStateReceiver> threadStateReceiverFactory) {
     this.actionKeyContext = actionKeyContext;
     this.outputArtifactsSeen = outputArtifactsSeen;
     this.outputArtifactsFromActionCache = outputArtifactsFromActionCache;
     this.statusReporterRef = statusReporterRef;
     this.sourceRootSupplier = sourceRootSupplier;
-    this.syscalls = syscalls;
+    this.syscallCache = syscallCache;
     this.threadStateReceiverFactory = threadStateReceiverFactory;
   }
 
@@ -546,7 +546,7 @@
         actionFileSystem,
         skyframeDepsResult,
         discoveredModulesPruner,
-        syscalls.get(),
+        syscallCache.get(),
         threadStateReceiverFactory.apply(actionLookupData));
   }
 
@@ -767,7 +767,7 @@
             env,
             actionFileSystem,
             discoveredModulesPruner,
-            syscalls.get(),
+            syscallCache.get(),
             threadStateReceiverFactory.apply(actionLookupData));
     if (actionFileSystem != null) {
       updateActionFileSystemContext(
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
index 705ae37..ec1a6f8 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
@@ -184,7 +184,7 @@
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.lib.vfs.Root;
 import com.google.devtools.build.lib.vfs.RootedPath;
-import com.google.devtools.build.lib.vfs.UnixGlob;
+import com.google.devtools.build.lib.vfs.SyscallCache;
 import com.google.devtools.build.skyframe.CycleInfo;
 import com.google.devtools.build.skyframe.CyclesReporter;
 import com.google.devtools.build.skyframe.Differencer;
@@ -294,8 +294,8 @@
 
   // AtomicReferences are used here as mutable boxes shared with value builders.
   private final AtomicBoolean showLoadingProgress = new AtomicBoolean();
-  protected final AtomicReference<UnixGlob.FilesystemCalls> syscalls =
-      new AtomicReference<>(UnixGlob.DEFAULT_SYSCALLS);
+  protected final AtomicReference<SyscallCache> syscallCacheRef =
+      new AtomicReference<>(SyscallCache.NO_CACHE);
   protected final AtomicReference<PathPackageLocator> pkgLocator = new AtomicReference<>();
   protected final AtomicReference<ImmutableSet<PackageIdentifier>> deletedPackages =
       new AtomicReference<>(ImmutableSet.of());
@@ -422,15 +422,15 @@
     this.shouldUnblockCpuWorkWhenFetchingDeps = shouldUnblockCpuWorkWhenFetchingDeps;
     this.skyKeyStateReceiver = skyKeyStateReceiver;
     this.bugReporter = bugReporter;
-    this.pkgFactory.setSyscalls(syscalls);
+    this.pkgFactory.setSyscallCache(syscallCacheRef::get);
     this.workspaceStatusActionFactory = workspaceStatusActionFactory;
     this.packageManager =
         new SkyframePackageManager(
             new SkyframePackageLoader(),
             new QueryTransitivePackagePreloader(
                 () -> memoizingEvaluator, this::newEvaluationContextBuilder),
-            syscalls,
-            pkgLocator,
+            syscallCacheRef::get,
+            pkgLocator::get,
             numPackagesLoaded);
     this.fileSystem = fileSystem;
     this.directories = Preconditions.checkNotNull(directories);
@@ -446,7 +446,7 @@
             outputArtifactsFromActionCache,
             statusReporterRef,
             this::getPathEntries,
-            syscalls,
+            syscallCacheRef::get,
             skyKeyStateReceiver::makeThreadStateReceiver);
     this.artifactFactory =
         new ArtifactFactory(
@@ -606,7 +606,7 @@
     map.put(SkyFunctions.BUILD_INFO, new WorkspaceStatusFunction(this::makeWorkspaceStatusAction));
     map.put(SkyFunctions.COVERAGE_REPORT, new CoverageReportFunction(actionKeyContext));
     ActionExecutionFunction actionExecutionFunction =
-        new ActionExecutionFunction(skyframeActionExecutor, directories, tsgm, bugReporter);
+        new ActionExecutionFunction(skyframeActionExecutor, directories, tsgm::get, bugReporter);
     map.put(SkyFunctions.ACTION_EXECUTION, actionExecutionFunction);
     this.actionExecutionFunction = actionExecutionFunction;
     map.put(
@@ -651,11 +651,11 @@
   }
 
   protected SkyFunction newFileStateFunction() {
-    return new FileStateFunction(tsgm, syscalls, externalFilesHelper);
+    return new FileStateFunction(tsgm::get, syscallCacheRef::get, externalFilesHelper);
   }
 
   protected SkyFunction newDirectoryListingStateFunction() {
-    return new DirectoryListingStateFunction(externalFilesHelper, syscalls);
+    return new DirectoryListingStateFunction(externalFilesHelper, syscallCacheRef::get);
   }
 
   protected GlobFunction newGlobFunction() {
@@ -700,8 +700,8 @@
     return perBuildSyscallCache;
   }
 
-  public AtomicReference<UnixGlob.FilesystemCalls> getSyscalls() {
-    return syscalls;
+  public final SyscallCache getCurrentSyscallCache() {
+    return syscallCacheRef.get();
   }
 
   @ThreadCompatible
@@ -1364,7 +1364,7 @@
         starlarkSemantics.getBool(BuildLanguageOptions.EXPERIMENTAL_SIBLING_REPOSITORY_LAYOUT));
     setPackageLocator(pkgLocator);
 
-    syscalls.set(getPerBuildSyscallCache(packageOptions.globbingThreads));
+    syscallCacheRef.set(getPerBuildSyscallCache(packageOptions.globbingThreads));
     this.pkgFactory.setGlobbingThreads(packageOptions.globbingThreads);
     this.pkgFactory.setMaxDirectoriesToEagerlyVisitInGlobbing(
         packageOptions.maxDirectoriesToEagerlyVisitInGlobbing);
@@ -2245,7 +2245,7 @@
 
   @VisibleForTesting
   public final void turnOffSyscallCacheForTesting() {
-    syscalls.set(UnixGlob.DEFAULT_SYSCALLS);
+    syscallCacheRef.set(SyscallCache.NO_CACHE);
   }
 
   protected abstract void invalidateFilesUnderPathForTestingImpl(
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframePackageManager.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframePackageManager.java
index ce43277..2b78b67 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframePackageManager.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframePackageManager.java
@@ -32,28 +32,28 @@
 import com.google.devtools.build.lib.pkgcache.QueryTransitivePackagePreloader;
 import com.google.devtools.build.lib.skyframe.SkyframeExecutor.SkyframePackageLoader;
 import com.google.devtools.build.lib.vfs.Path;
-import com.google.devtools.build.lib.vfs.UnixGlob;
+import com.google.devtools.build.lib.vfs.SyscallCache;
 import java.io.PrintStream;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
 
 class SkyframePackageManager implements PackageManager, CachingPackageLocator {
   private final SkyframePackageLoader packageLoader;
   private final QueryTransitivePackagePreloader transitiveLoader;
-  private final AtomicReference<UnixGlob.FilesystemCalls> syscalls;
-  private final AtomicReference<PathPackageLocator> pkgLocator;
+  private final Supplier<SyscallCache> syscallCache;
+  private final Supplier<PathPackageLocator> pkgLocator;
   private final AtomicInteger numPackagesLoaded;
 
   public SkyframePackageManager(
       SkyframePackageLoader packageLoader,
       QueryTransitivePackagePreloader transitiveLoader,
-      AtomicReference<UnixGlob.FilesystemCalls> syscalls,
-      AtomicReference<PathPackageLocator> pkgLocator,
+      Supplier<SyscallCache> syscallCache,
+      Supplier<PathPackageLocator> pkgLocator,
       AtomicInteger numPackagesLoaded) {
     this.packageLoader = packageLoader;
     this.transitiveLoader = transitiveLoader;
     this.pkgLocator = pkgLocator;
-    this.syscalls = syscalls;
+    this.syscallCache = syscallCache;
     this.numPackagesLoaded = numPackagesLoaded;
   }
 
@@ -99,7 +99,7 @@
     // TODO(bazel-team): Use a PackageLookupValue here [skyframe-loading]
     // TODO(bazel-team): The implementation in PackageCache also checks for duplicate packages, see
     // BuildFileCache#getBuildFile [skyframe-loading]
-    return pkgLocator.get().getPackageBuildFileNullable(packageName, syscalls);
+    return pkgLocator.get().getPackageBuildFileNullable(packageName, syscallCache.get());
   }
 
   @Override
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/packages/AbstractPackageLoader.java b/src/main/java/com/google/devtools/build/lib/skyframe/packages/AbstractPackageLoader.java
index 54f5c51..1f13bc1 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/packages/AbstractPackageLoader.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/packages/AbstractPackageLoader.java
@@ -86,7 +86,6 @@
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.lib.vfs.Root;
-import com.google.devtools.build.lib.vfs.UnixGlob.FilesystemCalls;
 import com.google.devtools.build.skyframe.Differencer;
 import com.google.devtools.build.skyframe.ErrorInfo;
 import com.google.devtools.build.skyframe.EvaluationContext;
@@ -435,14 +434,10 @@
   protected abstract ActionOnIOExceptionReadingBuildFile getActionOnIOExceptionReadingBuildFile();
 
   private ImmutableMap<SkyFunctionName, SkyFunction> makeFreshSkyFunctions() {
-    AtomicReference<TimestampGranularityMonitor> tsgm =
-        new AtomicReference<>(new TimestampGranularityMonitor(BlazeClock.instance()));
-    AtomicReference<FilesystemCalls> syscallCacheRef =
-        new AtomicReference<>(
-            PerBuildSyscallCache.newBuilder()
-                .setInitialCapacity(nonSkyframeGlobbingThreads)
-                .build());
-    pkgFactory.setSyscalls(syscallCacheRef);
+    TimestampGranularityMonitor tsgm = new TimestampGranularityMonitor(BlazeClock.instance());
+    PerBuildSyscallCache syscallCache =
+        PerBuildSyscallCache.newBuilder().setInitialCapacity(nonSkyframeGlobbingThreads).build();
+    pkgFactory.setSyscallCache(() -> syscallCache);
     pkgFactory.setMaxDirectoriesToEagerlyVisitInGlobbing(
         MAX_DIRECTORIES_TO_EAGERLY_VISIT_IN_GLOBBING);
     CachingPackageLocator cachingPackageLocator =
@@ -450,7 +445,7 @@
           @Override
           @Nullable
           public Path getBuildFileForPackage(PackageIdentifier packageName) {
-            return pkgLocatorRef.get().getPackageBuildFileNullable(packageName, syscallCacheRef);
+            return pkgLocatorRef.get().getPackageBuildFileNullable(packageName, syscallCache);
           }
 
           @Override
@@ -464,7 +459,7 @@
         .put(SkyFunctions.PRECOMPUTED, new PrecomputedFunction())
         .put(
             FileStateValue.FILE_STATE,
-            new FileStateFunction(tsgm, syscallCacheRef, externalFilesHelper))
+            new FileStateFunction(() -> tsgm, () -> syscallCache, externalFilesHelper))
         .put(FileSymlinkCycleUniquenessFunction.NAME, new FileSymlinkCycleUniquenessFunction())
         .put(
             FileSymlinkInfiniteExpansionUniquenessFunction.NAME,
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/packages/BazelPackageLoader.java b/src/main/java/com/google/devtools/build/lib/skyframe/packages/BazelPackageLoader.java
index d3e6b2a..bacc17b 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/packages/BazelPackageLoader.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/packages/BazelPackageLoader.java
@@ -41,7 +41,7 @@
 import com.google.devtools.build.lib.skyframe.SkyFunctions;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.Root;
-import com.google.devtools.build.lib.vfs.UnixGlob;
+import com.google.devtools.build.lib.vfs.SyscallCache;
 import com.google.devtools.build.skyframe.SkyFunction;
 import com.google.devtools.build.skyframe.SkyFunctionName;
 import java.util.Optional;
@@ -117,7 +117,7 @@
               .put(
                   SkyFunctions.DIRECTORY_LISTING_STATE,
                   new DirectoryListingStateFunction(
-                      externalFilesHelper, new AtomicReference<>(UnixGlob.DEFAULT_SYSCALLS)))
+                      externalFilesHelper, () -> SyscallCache.NO_CACHE))
               .put(SkyFunctions.ACTION_ENVIRONMENT_VARIABLE, new ActionEnvironmentFunction())
               .put(SkyFunctions.DIRECTORY_LISTING, new DirectoryListingFunction())
               .put(
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/SyscallCache.java b/src/main/java/com/google/devtools/build/lib/vfs/SyscallCache.java
new file mode 100644
index 0000000..752197a
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/vfs/SyscallCache.java
@@ -0,0 +1,68 @@
+// Copyright 2022 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.vfs;
+
+import java.io.IOException;
+import java.util.Collection;
+
+/**
+ * Centralized point to perform filesystem calls, to promote caching. Ideally all filesystem
+ * operations would be cached in Skyframe, but even then, implementations of this interface may do
+ * batch operations and prefetching to improve performance.
+ */
+public interface SyscallCache {
+  SyscallCache NO_CACHE =
+      new SyscallCache() {
+        @Override
+        public Collection<Dirent> readdir(Path path) throws IOException {
+          return path.readdir(Symlinks.NOFOLLOW);
+        }
+
+        @Override
+        public FileStatus statIfFound(Path path, Symlinks symlinks) throws IOException {
+          return path.statIfFound(symlinks);
+        }
+
+        @Override
+        public Dirent.Type getType(Path path, Symlinks symlinks) throws IOException {
+          return statusToDirentType(statIfFound(path, symlinks));
+        }
+      };
+
+  /** Gets directory entries and their types. Does not follow symlinks. */
+  Collection<Dirent> readdir(Path path) throws IOException;
+
+  /** Returns the stat() for the given path, or null. */
+  FileStatus statIfFound(Path path, Symlinks symlinks) throws IOException;
+
+  /**
+   * Returns the type of a specific file. This may be answered using stat() or readdir(). Returns
+   * null if the path does not exist.
+   */
+  Dirent.Type getType(Path path, Symlinks symlinks) throws IOException;
+
+  static Dirent.Type statusToDirentType(FileStatus status) {
+    if (status == null) {
+      return null;
+    } else if (status.isFile()) {
+      return Dirent.Type.FILE;
+    } else if (status.isDirectory()) {
+      return Dirent.Type.DIRECTORY;
+    } else if (status.isSymbolicLink()) {
+      return Dirent.Type.SYMLINK;
+    }
+    return Dirent.Type.UNKNOWN;
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/UnixGlob.java b/src/main/java/com/google/devtools/build/lib/vfs/UnixGlob.java
index 92f1c33..bbbf535 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/UnixGlob.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/UnixGlob.java
@@ -14,6 +14,8 @@
 
 package com.google.devtools.build.lib.vfs;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Joiner;
 import com.google.common.base.Preconditions;
@@ -74,7 +76,7 @@
       Path base,
       Collection<String> patterns,
       UnixGlobPathDiscriminator pathDiscriminator,
-      FilesystemCalls syscalls,
+      SyscallCache syscalls,
       Executor executor)
       throws IOException, InterruptedException, BadPattern {
     GlobVisitor visitor = new GlobVisitor(executor);
@@ -85,7 +87,7 @@
       Path base,
       Collection<String> patterns,
       UnixGlobPathDiscriminator pathDiscriminator,
-      FilesystemCalls syscalls,
+      SyscallCache syscalls,
       Executor executor)
       throws IOException, BadPattern {
     GlobVisitor visitor = new GlobVisitor(executor);
@@ -96,7 +98,7 @@
       Path base,
       Collection<String> patterns,
       UnixGlobPathDiscriminator pathDiscriminator,
-      FilesystemCalls syscalls,
+      SyscallCache syscalls,
       Executor executor)
       throws IOException, InterruptedException, BadPattern {
     GlobVisitor visitor = new GlobVisitor(executor);
@@ -108,7 +110,7 @@
       Path base,
       Collection<String> patterns,
       UnixGlobPathDiscriminator pathDiscriminator,
-      FilesystemCalls syscalls,
+      SyscallCache syscalls,
       Executor executor)
       throws BadPattern {
     Preconditions.checkNotNull(executor, "%s %s", base, patterns);
@@ -278,57 +280,6 @@
     return Pattern.compile(regexp.toString());
   }
 
-  /**
-   * Filesystem calls required for glob().
-   */
-  public interface FilesystemCalls {
-    /** Get directory entries and their types. Does not follow symlinks. */
-    Collection<Dirent> readdir(Path path) throws IOException;
-
-    /** Return the stat() for the given path, or null. */
-    FileStatus statIfFound(Path path, Symlinks symlinks) throws IOException;
-
-    /**
-     * Return the type of a specific file. This may be answered using stat() or readdir(). Returns
-     * null if the path does not exist.
-     */
-    Dirent.Type getType(Path path, Symlinks symlinks) throws IOException;
-  }
-
-  public static final FilesystemCalls DEFAULT_SYSCALLS =
-      new FilesystemCalls() {
-        @Override
-        public Collection<Dirent> readdir(Path path) throws IOException {
-          return path.readdir(Symlinks.NOFOLLOW);
-        }
-
-        @Override
-        public FileStatus statIfFound(Path path, Symlinks symlinks) throws IOException {
-          return path.statIfFound(symlinks);
-        }
-
-        @Override
-        public Dirent.Type getType(Path path, Symlinks symlinks) throws IOException {
-          return statusToDirentType(statIfFound(path, symlinks));
-        }
-      };
-
-  public static Dirent.Type statusToDirentType(FileStatus status) {
-    if (status == null) {
-      return null;
-    } else if (status.isFile()) {
-      return Dirent.Type.FILE;
-    } else if (status.isDirectory()) {
-      return Dirent.Type.DIRECTORY;
-    } else if (status.isSymbolicLink()) {
-      return Dirent.Type.SYMLINK;
-    }
-    return Dirent.Type.UNKNOWN;
-  }
-
-  public static final AtomicReference<FilesystemCalls> DEFAULT_SYSCALLS_REF =
-      new AtomicReference<>(DEFAULT_SYSCALLS);
-
   public static Builder forPath(Path path) {
     return new Builder(path);
   }
@@ -339,12 +290,11 @@
    *
    */
   public static class Builder {
-    private Path base;
-    private List<String> patterns;
+    private final Path base;
+    private final List<String> patterns;
     private UnixGlobPathDiscriminator pathDiscriminator = DEFAULT_DISCRIMINATOR;
     private Executor executor;
-    private AtomicReference<? extends FilesystemCalls> syscalls =
-        new AtomicReference<>(DEFAULT_SYSCALLS);
+    private SyscallCache syscallCache = SyscallCache.NO_CACHE;
 
     /**
      * Creates a glob builder with the given base path.
@@ -384,13 +334,9 @@
       return this;
     }
 
-    /**
-     * Sets the FilesystemCalls interface to use on this glob().
-     */
-    public Builder setFilesystemCalls(AtomicReference<? extends FilesystemCalls> syscalls) {
-      this.syscalls = (syscalls == null)
-          ? new AtomicReference<FilesystemCalls>(DEFAULT_SYSCALLS)
-          : syscalls;
+    /** Sets the SyscallCache interface to use on this glob(). */
+    public Builder setFilesystemCalls(SyscallCache syscallCache) {
+      this.syscallCache = checkNotNull(syscallCache);
       return this;
     }
 
@@ -422,8 +368,7 @@
 
     /** Executes the glob. */
     public List<Path> glob() throws IOException, BadPattern {
-      return globInternalUninterruptible(
-          base, patterns, pathDiscriminator, syscalls.get(), executor);
+      return globInternalUninterruptible(base, patterns, pathDiscriminator, syscallCache, executor);
     }
 
     /**
@@ -432,14 +377,14 @@
      * @throws InterruptedException if the thread is interrupted.
      */
     public List<Path> globInterruptible() throws IOException, InterruptedException, BadPattern {
-      return globInternal(base, patterns, pathDiscriminator, syscalls.get(), executor);
+      return globInternal(base, patterns, pathDiscriminator, syscallCache, executor);
     }
 
     @VisibleForTesting
     public long globInterruptibleAndReturnNumGlobTasksForTesting()
         throws IOException, InterruptedException, BadPattern {
       return globInternalAndReturnNumGlobTasksForTesting(
-          base, patterns, pathDiscriminator, syscalls.get(), executor);
+          base, patterns, pathDiscriminator, syscallCache, executor);
     }
 
     /**
@@ -447,7 +392,7 @@
      * non-null argument.
      */
     public Future<List<Path>> globAsync() throws BadPattern {
-      return globAsyncInternal(base, patterns, pathDiscriminator, syscalls.get(), executor);
+      return globAsyncInternal(base, patterns, pathDiscriminator, syscallCache, executor);
     }
   }
 
@@ -531,7 +476,7 @@
         Path base,
         Collection<String> patterns,
         UnixGlobPathDiscriminator pathDiscriminator,
-        FilesystemCalls syscalls)
+        SyscallCache syscalls)
         throws IOException, InterruptedException, BadPattern {
       try {
         return globAsync(base, patterns, pathDiscriminator, syscalls).get();
@@ -546,7 +491,7 @@
         Path base,
         Collection<String> patterns,
         UnixGlobPathDiscriminator pathDiscriminator,
-        FilesystemCalls syscalls)
+        SyscallCache syscalls)
         throws IOException, BadPattern {
       try {
         return Uninterruptibles.getUninterruptibly(
@@ -571,7 +516,7 @@
         Path base,
         Collection<String> patterns,
         UnixGlobPathDiscriminator pathDiscriminator,
-        FilesystemCalls syscalls)
+        SyscallCache syscalls)
         throws BadPattern {
       FileStatus baseStat;
       try {
@@ -710,12 +655,12 @@
     private class GlobTaskContext {
       private final String[] patternParts;
       private final UnixGlobPathDiscriminator pathDiscriminator;
-      private final FilesystemCalls syscalls;
+      private final SyscallCache syscalls;
 
       GlobTaskContext(
           String[] patternParts,
           UnixGlobPathDiscriminator pathDiscriminator,
-          FilesystemCalls syscalls) {
+          SyscallCache syscalls) {
         this.patternParts = patternParts;
         this.pathDiscriminator = pathDiscriminator;
         this.syscalls = syscalls;
@@ -766,7 +711,7 @@
       private RecursiveGlobTaskContext(
           String[] patternParts,
           UnixGlobPathDiscriminator pathDiscriminator,
-          FilesystemCalls syscalls) {
+          SyscallCache syscalls) {
         super(patternParts, pathDiscriminator, syscalls);
       }