summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java109
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java72
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java94
3 files changed, 168 insertions, 107 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
index 07e38147f7..4aa2edff38 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
@@ -28,7 +28,6 @@ import static org.eclipse.jgit.lib.Ref.Storage.PACKED;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
@@ -892,38 +891,27 @@ public class RefDirectory extends RefDatabase {
}
private PackedRefList readPackedRefs() throws IOException {
- int maxStaleRetries = 5;
- int retries = 0;
- while (true) {
- final FileSnapshot snapshot = FileSnapshot.save(packedRefsFile);
- final MessageDigest digest = Constants.newMessageDigest();
- try (BufferedReader br = new BufferedReader(new InputStreamReader(
- new DigestInputStream(new FileInputStream(packedRefsFile),
- digest),
- UTF_8))) {
- try {
- return new PackedRefList(parsePackedRefs(br), snapshot,
- ObjectId.fromRaw(digest.digest()));
- } catch (IOException e) {
- if (FileUtils.isStaleFileHandleInCausalChain(e)
- && retries < maxStaleRetries) {
- if (LOG.isDebugEnabled()) {
- LOG.debug(MessageFormat.format(
- JGitText.get().packedRefsHandleIsStale,
- Integer.valueOf(retries)), e);
+ try {
+ PackedRefList result = FileUtils.readWithRetries(packedRefsFile,
+ f -> {
+ FileSnapshot snapshot = FileSnapshot.save(f);
+ MessageDigest digest = Constants.newMessageDigest();
+ try (BufferedReader br = new BufferedReader(
+ new InputStreamReader(
+ new DigestInputStream(
+ new FileInputStream(f), digest),
+ UTF_8))) {
+ return new PackedRefList(parsePackedRefs(br),
+ snapshot,
+ ObjectId.fromRaw(digest.digest()));
}
- retries++;
- continue;
- }
- throw e;
- }
- } catch (FileNotFoundException noPackedRefs) {
- if (packedRefsFile.exists()) {
- throw noPackedRefs;
- }
- // Ignore it and leave the new list empty.
- return NO_PACKED_REFS;
- }
+ });
+ return result != null ? result : NO_PACKED_REFS;
+ } catch (IOException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new IOException(MessageFormat
+ .format(JGitText.get().cannotReadFile, packedRefsFile), e);
}
}
@@ -1090,40 +1078,55 @@ public class RefDirectory extends RefDatabase {
}
final int limit = 4096;
- final byte[] buf;
- FileSnapshot otherSnapshot = FileSnapshot.save(path);
- try {
- buf = IO.readSome(path, limit);
- } catch (FileNotFoundException noFile) {
- if (path.isFile()) {
- throw noFile;
+
+ class LooseItems {
+ final FileSnapshot snapshot;
+
+ final byte[] buf;
+
+ LooseItems(FileSnapshot snapshot, byte[] buf) {
+ this.snapshot = snapshot;
+ this.buf = buf;
}
- return null; // doesn't exist or no file; not a reference.
}
-
- int n = buf.length;
+ LooseItems loose = null;
+ try {
+ loose = FileUtils.readWithRetries(path,
+ f -> new LooseItems(FileSnapshot.save(f),
+ IO.readSome(f, limit)));
+ } catch (IOException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new IOException(
+ MessageFormat.format(JGitText.get().cannotReadFile, path),
+ e);
+ }
+ if (loose == null) {
+ return null;
+ }
+ int n = loose.buf.length;
if (n == 0)
return null; // empty file; not a reference.
- if (isSymRef(buf, n)) {
+ if (isSymRef(loose.buf, n)) {
if (n == limit)
return null; // possibly truncated ref
// trim trailing whitespace
- while (0 < n && Character.isWhitespace(buf[n - 1]))
+ while (0 < n && Character.isWhitespace(loose.buf[n - 1]))
n--;
if (n < 6) {
- String content = RawParseUtils.decode(buf, 0, n);
+ String content = RawParseUtils.decode(loose.buf, 0, n);
throw new IOException(MessageFormat.format(JGitText.get().notARef, name, content));
}
- final String target = RawParseUtils.decode(buf, 5, n);
+ final String target = RawParseUtils.decode(loose.buf, 5, n);
if (ref != null && ref.isSymbolic()
&& ref.getTarget().getName().equals(target)) {
assert(currentSnapshot != null);
- currentSnapshot.setClean(otherSnapshot);
+ currentSnapshot.setClean(loose.snapshot);
return ref;
}
- return newSymbolicRef(otherSnapshot, name, target);
+ return newSymbolicRef(loose.snapshot, name, target);
}
if (n < OBJECT_ID_STRING_LENGTH)
@@ -1131,23 +1134,23 @@ public class RefDirectory extends RefDatabase {
final ObjectId id;
try {
- id = ObjectId.fromString(buf, 0);
+ id = ObjectId.fromString(loose.buf, 0);
if (ref != null && !ref.isSymbolic()
&& id.equals(ref.getTarget().getObjectId())) {
assert(currentSnapshot != null);
- currentSnapshot.setClean(otherSnapshot);
+ currentSnapshot.setClean(loose.snapshot);
return ref;
}
} catch (IllegalArgumentException notRef) {
- while (0 < n && Character.isWhitespace(buf[n - 1]))
+ while (0 < n && Character.isWhitespace(loose.buf[n - 1]))
n--;
- String content = RawParseUtils.decode(buf, 0, n);
+ String content = RawParseUtils.decode(loose.buf, 0, n);
throw new IOException(MessageFormat.format(JGitText.get().notARef,
name, content), notRef);
}
- return new LooseUnpeeled(otherSnapshot, name, id);
+ return new LooseUnpeeled(loose.snapshot, name, id);
}
private static boolean isSymRef(byte[] buf, int n) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
index 7b5f00e4fe..567e40936a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
@@ -20,7 +20,6 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.ByteArrayOutputStream;
import java.io.File;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.MessageFormat;
@@ -37,15 +36,11 @@ import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FileUtils;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* The configuration file that is stored in the file of the file system.
*/
public class FileBasedConfig extends StoredConfig {
- private static final Logger LOG = LoggerFactory
- .getLogger(FileBasedConfig.class);
private final File configFile;
@@ -115,16 +110,15 @@ public class FileBasedConfig extends StoredConfig {
*/
@Override
public void load() throws IOException, ConfigInvalidException {
- final int maxRetries = 5;
- int retryDelayMillis = 20;
- int retries = 0;
- while (true) {
- final FileSnapshot oldSnapshot = snapshot;
- final FileSnapshot newSnapshot;
- // don't use config in this snapshot to avoid endless recursion
- newSnapshot = FileSnapshot.saveNoConfig(getFile());
- try {
- final byte[] in = IO.readFully(getFile());
+ try {
+ FileSnapshot[] lastSnapshot = { null };
+ Boolean wasRead = FileUtils.readWithRetries(getFile(), f -> {
+ final FileSnapshot oldSnapshot = snapshot;
+ final FileSnapshot newSnapshot;
+ // don't use config in this snapshot to avoid endless recursion
+ newSnapshot = FileSnapshot.saveNoConfig(f);
+ lastSnapshot[0] = newSnapshot;
+ final byte[] in = IO.readFully(f);
final ObjectId newHash = hash(in);
if (hash.equals(newHash)) {
if (oldSnapshot.equals(newSnapshot)) {
@@ -145,47 +139,17 @@ public class FileBasedConfig extends StoredConfig {
snapshot = newSnapshot;
hash = newHash;
}
- return;
- } catch (FileNotFoundException noFile) {
- // might be locked by another process (see exception Javadoc)
- if (retries < maxRetries && configFile.exists()) {
- if (LOG.isDebugEnabled()) {
- LOG.debug(MessageFormat.format(
- JGitText.get().configHandleMayBeLocked,
- Integer.valueOf(retries)), noFile);
- }
- try {
- Thread.sleep(retryDelayMillis);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- retries++;
- retryDelayMillis *= 2; // max wait 1260 ms
- continue;
- }
- if (configFile.exists()) {
- throw noFile;
- }
+ return Boolean.TRUE;
+ });
+ if (wasRead == null) {
clear();
- snapshot = newSnapshot;
- return;
- } catch (IOException e) {
- if (FileUtils.isStaleFileHandle(e)
- && retries < maxRetries) {
- if (LOG.isDebugEnabled()) {
- LOG.debug(MessageFormat.format(
- JGitText.get().configHandleIsStale,
- Integer.valueOf(retries)), e);
- }
- retries++;
- continue;
- }
- throw new IOException(MessageFormat
- .format(JGitText.get().cannotReadFile, getFile()), e);
- } catch (ConfigInvalidException e) {
- throw new ConfigInvalidException(MessageFormat
- .format(JGitText.get().cannotReadFile, getFile()), e);
+ snapshot = lastSnapshot[0];
}
+ } catch (IOException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new ConfigInvalidException(MessageFormat
+ .format(JGitText.get().cannotReadFile, getFile()), e);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
index b9dd9baa61..f013e7e095 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
@@ -17,6 +17,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InterruptedIOException;
import java.nio.channels.FileChannel;
import java.nio.file.AtomicMoveNotSupportedException;
import java.nio.file.CopyOption;
@@ -655,6 +656,99 @@ public class FileUtils {
}
/**
+ * Like a {@link java.util.function.Function} but throwing an
+ * {@link Exception}.
+ *
+ * @param <A>
+ * input type
+ * @param <B>
+ * output type
+ * @since 6.2
+ */
+ @FunctionalInterface
+ public interface IOFunction<A, B> {
+
+ /**
+ * Performs the function.
+ *
+ * @param t
+ * input to operate on
+ * @return the output
+ * @throws Exception
+ * if a problem occurs
+ */
+ B apply(A t) throws Exception;
+ }
+
+ private static void backOff(long delay, IOException cause)
+ throws IOException {
+ try {
+ Thread.sleep(delay);
+ } catch (InterruptedException e) {
+ IOException interruption = new InterruptedIOException();
+ interruption.initCause(e);
+ interruption.addSuppressed(cause);
+ Thread.currentThread().interrupt(); // Re-set flag
+ throw interruption;
+ }
+ }
+
+ /**
+ * Invokes the given {@link IOFunction}, performing a limited number of
+ * re-tries if exceptions occur that indicate either a stale NFS file handle
+ * or that indicate that the file may be written concurrently.
+ *
+ * @param <T>
+ * result type
+ * @param file
+ * to read
+ * @param reader
+ * for reading the file and creating an instance of {@code T}
+ * @return the result of the {@code reader}, or {@code null} if the file
+ * does not exist
+ * @throws Exception
+ * if a problem occurs
+ * @since 6.2
+ */
+ public static <T> T readWithRetries(File file,
+ IOFunction<File, ? extends T> reader)
+ throws Exception {
+ int maxStaleRetries = 5;
+ int retries = 0;
+ long backoff = 50;
+ while (true) {
+ try {
+ try {
+ return reader.apply(file);
+ } catch (IOException e) {
+ if (FileUtils.isStaleFileHandleInCausalChain(e)
+ && retries < maxStaleRetries) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(MessageFormat.format(
+ JGitText.get().packedRefsHandleIsStale,
+ Integer.valueOf(retries)), e);
+ }
+ retries++;
+ continue;
+ }
+ throw e;
+ }
+ } catch (FileNotFoundException noFile) {
+ if (!file.isFile()) {
+ return null;
+ }
+ // Probably Windows and some other thread is writing the file
+ // concurrently.
+ if (backoff > 1000) {
+ throw noFile;
+ }
+ backOff(backoff, noFile);
+ backoff *= 2; // 50, 100, 200, 400, 800 ms
+ }
+ }
+ }
+
+ /**
* @param file
* @return {@code true} if the passed file is a symbolic link
*/