Skip to content

Commit e0a92ad

Browse files
committed
Address comment
1 parent f201504 commit e0a92ad

File tree

5 files changed

+39
-47
lines changed

5 files changed

+39
-47
lines changed

src/main/java/com/google/devtools/build/lib/bazel/repository/cache/BUILD

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ java_library(
2323
deps = [
2424
"//src/main/java/com/google/devtools/build/lib/server:idle_task",
2525
"//src/main/java/com/google/devtools/build/lib/util:file_system_lock",
26-
"//src/main/java/com/google/devtools/build/lib/util:os",
2726
"//src/main/java/com/google/devtools/build/lib/vfs",
2827
"//third_party:guava",
2928
"//third_party:jsr305",

src/main/java/com/google/devtools/build/lib/bazel/repository/cache/DownloadCache.java

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,7 @@
2121
import com.google.common.hash.HashFunction;
2222
import com.google.common.hash.Hasher;
2323
import com.google.common.io.BaseEncoding;
24-
import com.google.devtools.build.lib.util.OS;
2524
import com.google.devtools.build.lib.vfs.DigestHashFunction;
26-
import com.google.devtools.build.lib.vfs.FileAccessException;
2725
import com.google.devtools.build.lib.vfs.FileSystemUtils;
2826
import com.google.devtools.build.lib.vfs.Path;
2927
import java.io.IOException;
@@ -243,19 +241,7 @@ private void storeCacheValue(
243241
Path tmpName = cacheEntry.getRelative(TMP_PREFIX + UUID.randomUUID());
244242
cacheEntry.createDirectoryAndParents();
245243
fileWriter.writeTo(tmpName);
246-
try {
247-
tmpName.renameTo(cacheValue);
248-
} catch (FileAccessException e) {
249-
// On Windows, atomically replacing a file that is currently opened (e.g. due to a concurrent
250-
// get on the cache) results in renameTo throwing this exception, which wraps an
251-
// AccessDeniedException. This case is benign since if the target path already exists, we know
252-
// that another thread won the race to place the file in the cache. As the exception is rather
253-
// generic and could result from other failure types, we rethrow the exception if the cache
254-
// entry hasn't been created.
255-
if (OS.getCurrent() != OS.WINDOWS || !cacheValue.exists()) {
256-
throw e;
257-
}
258-
}
244+
FileSystemUtils.renameToleratingConcurrentCreation(tmpName, cacheValue);
259245

260246
if (!Strings.isNullOrEmpty(canonicalId)) {
261247
String idHash = keyType.newHasher().putBytes(canonicalId.getBytes(UTF_8)).hash().toString();

src/main/java/com/google/devtools/build/lib/remote/disk/BUILD

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ java_library(
2424
"//src/main/java/com/google/devtools/build/lib/remote/util:digest_utils",
2525
"//src/main/java/com/google/devtools/build/lib/server:idle_task",
2626
"//src/main/java/com/google/devtools/build/lib/util:file_system_lock",
27-
"//src/main/java/com/google/devtools/build/lib/util:os",
2827
"//src/main/java/com/google/devtools/build/lib/vfs",
2928
"//third_party:flogger",
3029
"//third_party:guava",

src/main/java/com/google/devtools/build/lib/remote/disk/DiskCacheClient.java

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@
3535
import com.google.devtools.build.lib.remote.util.DigestOutputStream;
3636
import com.google.devtools.build.lib.remote.util.DigestUtil;
3737
import com.google.devtools.build.lib.remote.util.Utils;
38-
import com.google.devtools.build.lib.util.OS;
39-
import com.google.devtools.build.lib.vfs.FileAccessException;
38+
import com.google.devtools.build.lib.vfs.FileSystemUtils;
4039
import com.google.devtools.build.lib.vfs.Path;
4140
import com.google.protobuf.ByteString;
4241
import com.google.protobuf.ExtensionRegistryLite;
@@ -144,20 +143,7 @@ public void captureFile(Path src, Digest digest, Store store) throws IOException
144143
}
145144

146145
target.getParentDirectory().createDirectoryAndParents();
147-
try {
148-
src.renameTo(target);
149-
} catch (FileAccessException e) {
150-
// On Windows, atomically replacing a file that is currently opened (e.g. due to a
151-
// concurrent get on the cache) results in renameTo throwing this exception, which wraps an
152-
// AccessDeniedException. This case is benign since if the target path already exists, we
153-
// know that another thread won the race to place the file in the cache. As the exception is
154-
// rather generic and could result from other failure types, we rethrow the exception if the
155-
// cache entry hasn't been created.
156-
if (OS.getCurrent() != OS.WINDOWS || !target.exists()) {
157-
throw e;
158-
}
159-
src.delete();
160-
}
146+
FileSystemUtils.renameToleratingConcurrentCreation(src, target);
161147
}
162148

163149
private ListenableFuture<Void> download(Digest digest, OutputStream out, Store store) {
@@ -348,20 +334,7 @@ public void saveFile(Digest digest, Store store, InputStream in) throws IOExcept
348334
}
349335
}
350336
path.getParentDirectory().createDirectoryAndParents();
351-
try {
352-
temp.renameTo(path);
353-
} catch (FileAccessException e) {
354-
// On Windows, atomically replacing a file that is currently opened (e.g. due to a
355-
// concurrent get on the cache) results in renameTo throwing this exception, which wraps an
356-
// AccessDeniedException. This case is benign since if the target path already exists, we
357-
// know that another thread won the race to place the file in the cache. As the exception is
358-
// rather generic and could result from other failure types, we rethrow the exception if the
359-
// cache entry hasn't been created.
360-
if (OS.getCurrent() != OS.WINDOWS || !path.exists()) {
361-
throw e;
362-
}
363-
temp.delete();
364-
}
337+
FileSystemUtils.renameToleratingConcurrentCreation(temp, path);
365338
} catch (IOException e) {
366339
try {
367340
temp.delete();

src/main/java/com/google/devtools/build/lib/vfs/FileSystemUtils.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.google.common.io.ByteStreams;
2626
import com.google.devtools.build.lib.concurrent.ThreadSafety.ConditionallyThreadSafe;
2727
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
28+
import com.google.devtools.build.lib.util.OS;
2829
import com.google.devtools.build.lib.util.StringEncoding;
2930
import java.io.FileNotFoundException;
3031
import java.io.IOException;
@@ -489,6 +490,40 @@ public static MoveResult moveFile(Path from, Path to) throws IOException {
489490
}
490491
}
491492

493+
/**
494+
* Atomically renames a source file to a target file, tolerating the case where another thread has
495+
* concurrently created the target file (e.g. because it is known to have the same content in a
496+
* CAS-like structure).
497+
*
498+
* <p>This handles a Windows-specific edge case: when the target file is being read by another
499+
* process (e.g., during a concurrent cache lookup), the rename operation fails with a {@link
500+
* FileAccessException}. If the target file already exists when this happens, it means another
501+
* thread won the race to create it, so we can safely delete the source file.
502+
*
503+
* <p>The parent directories of the target file must already exist.
504+
*
505+
* @param source the file to rename
506+
* @param target the destination path
507+
*/
508+
@ThreadSafe
509+
public static void renameToleratingConcurrentCreation(Path source, Path target)
510+
throws IOException {
511+
try {
512+
source.renameTo(target);
513+
} catch (FileAccessException e) {
514+
// On Windows, atomically replacing a file that is currently opened (e.g. due to a concurrent
515+
// get on the cache) results in renameTo throwing this exception, which wraps an
516+
// AccessDeniedException. This case is benign since if the target path already exists, we know
517+
// that another thread won the race to place the file in the cache. As the exception is rather
518+
// generic and could result from other failure types, we rethrow the exception if the cache
519+
// entry hasn't been created.
520+
if (OS.getCurrent() != OS.WINDOWS || !target.exists()) {
521+
throw e;
522+
}
523+
source.delete();
524+
}
525+
}
526+
492527
/* Directory tree operations. */
493528

494529
/**

0 commit comments

Comments
 (0)