aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit/src/org/eclipse/jgit/util
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.jgit/src/org/eclipse/jgit/util')
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java94
1 files changed, 94 insertions, 0 deletions
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
*/