|
35 | 35 | import com.google.devtools.build.lib.remote.util.DigestOutputStream; |
36 | 36 | import com.google.devtools.build.lib.remote.util.DigestUtil; |
37 | 37 | 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 | 40 | import com.google.devtools.build.lib.vfs.Path; |
39 | 41 | import com.google.protobuf.ByteString; |
40 | 42 | import com.google.protobuf.ExtensionRegistryLite; |
@@ -138,7 +140,20 @@ public void captureFile(Path src, Digest digest, Store store) throws IOException |
138 | 140 | } |
139 | 141 |
|
140 | 142 | target.getParentDirectory().createDirectoryAndParents(); |
141 | | - src.renameTo(target); |
| 143 | + try { |
| 144 | + src.renameTo(target); |
| 145 | + } catch (FileAccessException e) { |
| 146 | + // On Windows, atomically replacing a file that is currently opened (e.g. due to a |
| 147 | + // concurrent get on the cache) results in renameTo throwing this exception, which wraps an |
| 148 | + // AccessDeniedException. This case is benign since if the target path already exists, we |
| 149 | + // know that another thread won the race to place the file in the cache. As the exception is |
| 150 | + // rather generic and could result from other failure types, we rethrow the exception if the |
| 151 | + // cache entry hasn't been created. |
| 152 | + if (OS.getCurrent() != OS.WINDOWS || !target.exists()) { |
| 153 | + throw e; |
| 154 | + } |
| 155 | + src.delete(); |
| 156 | + } |
142 | 157 | } |
143 | 158 |
|
144 | 159 | private ListenableFuture<Void> download(Digest digest, OutputStream out, Store store) { |
@@ -327,7 +342,20 @@ public void saveFile(Digest digest, Store store, InputStream in) throws IOExcept |
327 | 342 | } |
328 | 343 | } |
329 | 344 | path.getParentDirectory().createDirectoryAndParents(); |
330 | | - temp.renameTo(path); |
| 345 | + try { |
| 346 | + temp.renameTo(path); |
| 347 | + } catch (FileAccessException e) { |
| 348 | + // On Windows, atomically replacing a file that is currently opened (e.g. due to a |
| 349 | + // concurrent get on the cache) results in renameTo throwing this exception, which wraps an |
| 350 | + // AccessDeniedException. This case is benign since if the target path already exists, we |
| 351 | + // know that another thread won the race to place the file in the cache. As the exception is |
| 352 | + // rather generic and could result from other failure types, we rethrow the exception if the |
| 353 | + // cache entry hasn't been created. |
| 354 | + if (OS.getCurrent() != OS.WINDOWS || !path.exists()) { |
| 355 | + throw e; |
| 356 | + } |
| 357 | + temp.delete(); |
| 358 | + } |
331 | 359 | } catch (IOException e) { |
332 | 360 | try { |
333 | 361 | temp.delete(); |
|
0 commit comments