FS.detectSymlinkSupport: fix a race

When >1 JGit clients are instantiated concurrently, they may try to
create the same symlink at the same time. When that happens, the second
thread will return an error (because the symlink already exists) and
that `FS` instance will think that symlinks are not supported, causing
havoc.
motiejus-detectSymlinkSupport
Motiejus Jakštys 2023-12-28 16:33:28 +02:00
parent b1cc74b75b
commit ab2d63322f
2 changed files with 23 additions and 1 deletions

View File

@ -32,6 +32,8 @@ import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import org.eclipse.jgit.errors.CommandFailedException;
import org.eclipse.jgit.junit.MockSystemReader;
@ -195,6 +197,26 @@ public class FSTest {
SystemReader.getInstance().getDefaultCharset().name());
}
@Test
public void testConcurrentSymlinkSupport()
throws ExecutionException, InterruptedException {
FS fs1 = FS.DETECTED;
Assume.assumeTrue(fs1.supportsSymlinks());
// 2 is somewhat reliable, 3 is very reliable.
int numberOfThreads = 3;
CompletableFuture<Boolean>[] futures = new CompletableFuture[numberOfThreads];
for (int i = 0; i < numberOfThreads; i++) {
futures[i] = CompletableFuture.supplyAsync(() -> {
FS fs2 = fs1.newInstance();
return fs2.supportsSymlinks();
});
}
for (int i = 0; i < numberOfThreads; i++) {
assertTrue(futures[i].get());
}
}
@Test
public void testFsTimestampResolution() throws Exception {
DateTimeFormatter formatter = DateTimeFormatter

View File

@ -1024,7 +1024,7 @@ public abstract class FS {
File tempFile = null;
try {
tempFile = File.createTempFile("tempsymlinktarget", ""); //$NON-NLS-1$ //$NON-NLS-2$
File linkName = new File(tempFile.getParentFile(), "tempsymlink"); //$NON-NLS-1$
File linkName = new File(tempFile.getPath() + "-thelink"); //$NON-NLS-1$
createSymLink(linkName, tempFile.getPath());
supportSymlinks = Boolean.TRUE;
linkName.delete();