aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit/src/org/eclipse/jgit
diff options
context:
space:
mode:
authorMatthias Sohn <matthias.sohn@sap.com>2023-02-22 01:26:36 +0100
committerMatthias Sohn <matthias.sohn@sap.com>2023-02-22 01:26:36 +0100
commit4c111e59d00a4d4a1740603d67b63ebf5d755c71 (patch)
tree0e7142ecfb9fd5d6dd510d76664b9646ad186d9c /org.eclipse.jgit/src/org/eclipse/jgit
parent07a9eb06ff9d223af92063a643095ecba2c3d28a (diff)
parent2a2a208fa1651e0558755c4804a2a9db1433a345 (diff)
downloadjgit-4c111e59d00a4d4a1740603d67b63ebf5d755c71.tar.gz
jgit-4c111e59d00a4d4a1740603d67b63ebf5d755c71.zip
Merge branch 'stable-6.0' into stable-6.1
* stable-6.0: Use Java 11 ProcessHandle to get pid of the current process Acquire file lock "gc.pid" before running gc Silence API errors introduced by 9424052f Change-Id: Ib9a2419253ffcbc90874adbfdb8129fee3178210
Diffstat (limited to 'org.eclipse.jgit/src/org/eclipse/jgit')
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java169
2 files changed, 166 insertions, 7 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
index bf65dae03b..2adde0a5d0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -165,6 +165,7 @@ public class JGitText extends TranslationBundle {
/***/ public String cloneNonEmptyDirectory;
/***/ public String closeLockTokenFailed;
/***/ public String closed;
+ /***/ public String closePidLockFailed;
/***/ public String collisionOn;
/***/ public String commandClosedStderrButDidntExit;
/***/ public String commandRejectedByHook;
@@ -334,6 +335,7 @@ public class JGitText extends TranslationBundle {
/***/ public String expectedReportForRefNotReceived;
/***/ public String failedAtomicFileCreation;
/***/ public String failedCreateLockFile;
+ /***/ public String failedPidLock;
/***/ public String failedReadHttpsProtocols;
/***/ public String failedToDetermineFilterDefinition;
/***/ public String failedToConvert;
@@ -353,6 +355,7 @@ public class JGitText extends TranslationBundle {
/***/ public String flagNotFromThis;
/***/ public String flagsAlreadyCreated;
/***/ public String funnyRefname;
+ /***/ public String gcAlreadyRunning;
/***/ public String gcFailed;
/***/ public String gcTooManyUnpruned;
/***/ public String headRequiredToStash;
@@ -712,6 +715,7 @@ public class JGitText extends TranslationBundle {
/***/ public String sslTrustForRepo;
/***/ public String sslTrustNow;
/***/ public String sslVerifyCannotSave;
+ /***/ public String stalePidLock;
/***/ public String staleRevFlagsOn;
/***/ public String startingReadStageWithoutWrittenRequestDataPendingIsNotSupported;
/***/ public String stashApplyConflict;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
index 8c0aca5153..080af97cbc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
@@ -20,9 +20,16 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
+import java.io.RandomAccessFile;
import java.io.StringWriter;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.nio.channels.OverlappingFileLockException;
+import java.nio.charset.StandardCharsets;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
@@ -44,6 +51,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.CompletableFuture;
@@ -84,8 +92,11 @@ import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.pack.PackConfig;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.TreeFilter;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.FS.LockToken;
import org.eclipse.jgit.util.FileUtils;
import org.eclipse.jgit.util.GitDateParser;
+import org.eclipse.jgit.util.StringUtils;
import org.eclipse.jgit.util.SystemReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -262,13 +273,18 @@ public class GC {
if (automatic && !needGc()) {
return Collections.emptyList();
}
- pm.start(6 /* tasks */);
- packRefs();
- // TODO: implement reflog_expire(pm, repo);
- Collection<Pack> newPacks = repack();
- prune(Collections.emptySet());
- // TODO: implement rerere_gc(pm);
- return newPacks;
+ try (PidLock lock = new PidLock()) {
+ if (!lock.lock()) {
+ return Collections.emptyList();
+ }
+ pm.start(6 /* tasks */);
+ packRefs();
+ // TODO: implement reflog_expire(pm, repo);
+ Collection<Pack> newPacks = repack();
+ prune(Collections.emptySet());
+ // TODO: implement rerere_gc(pm);
+ return newPacks;
+ }
}
/**
@@ -1596,4 +1612,143 @@ public class GC {
return repo.getConfig().getInt(ConfigConstants.CONFIG_GC_SECTION,
ConfigConstants.CONFIG_KEY_AUTO, DEFAULT_AUTOLIMIT);
}
+
+ private class PidLock implements AutoCloseable {
+
+ private static final String GC_PID = "gc.pid"; //$NON-NLS-1$
+
+ private final Path pidFile;
+
+ private LockToken token;
+
+ private FileLock lock;
+
+ private RandomAccessFile f;
+
+ private FileChannel channel;
+
+ PidLock() {
+ pidFile = repo.getDirectory().toPath().resolve(GC_PID);
+ }
+
+ boolean lock() {
+ if (Files.exists(pidFile)) {
+ Instant mtime = FS.DETECTED
+ .lastModifiedInstant(pidFile.toFile());
+ Instant twelveHoursAgo = Instant.now().minus(12,
+ ChronoUnit.HOURS);
+ if (mtime.compareTo(twelveHoursAgo) > 0) {
+ gcAlreadyRunning();
+ return false;
+ }
+ LOG.warn(MessageFormat.format(JGitText.get().stalePidLock,
+ pidFile, mtime));
+ }
+ try {
+ token = FS.DETECTED.createNewFileAtomic(pidFile.toFile());
+ f = new RandomAccessFile(pidFile.toFile(), "rw"); //$NON-NLS-1$
+ channel = f.getChannel();
+ lock = channel.tryLock();
+ if (lock == null) {
+ failedToLock();
+ return false;
+ }
+ channel.write(ByteBuffer
+ .wrap(getProcDesc().getBytes(StandardCharsets.UTF_8)));
+ Thread cleanupHook = new Thread(() -> close());
+ try {
+ Runtime.getRuntime().addShutdownHook(cleanupHook);
+ } catch (IllegalStateException e) {
+ // ignore - the VM is already shutting down
+ }
+ } catch (IOException | OverlappingFileLockException e) {
+ try {
+ failedToLock();
+ } catch (Exception e1) {
+ LOG.error(
+ MessageFormat.format(
+ JGitText.get().closePidLockFailed, pidFile),
+ e1);
+ }
+ return false;
+ }
+ return true;
+ }
+
+ private void failedToLock() {
+ close();
+ LOG.error(MessageFormat.format(JGitText.get().failedPidLock,
+ pidFile));
+ }
+
+ private void gcAlreadyRunning() {
+ close();
+ try {
+ Optional<String> s = Files.lines(pidFile).findFirst();
+ String machine = null;
+ String pid = null;
+ if (s.isPresent()) {
+ String[] c = s.get().split("\\s+"); //$NON-NLS-1$
+ pid = c[0];
+ machine = c[1];
+ }
+ if (!StringUtils.isEmptyOrNull(machine)
+ && !StringUtils.isEmptyOrNull(pid)) {
+ LOG.error(MessageFormat.format(
+ JGitText.get().gcAlreadyRunning, machine, pid));
+ return;
+ }
+ } catch (IOException e) {
+ // ignore
+ }
+ LOG.error(MessageFormat.format(JGitText.get().failedPidLock,
+ pidFile));
+ }
+
+ private String getProcDesc() {
+ StringBuffer s = new StringBuffer(Long.toString(getPID()));
+ s.append(' ');
+ s.append(getHostName());
+ return s.toString();
+ }
+
+ private long getPID() {
+ return ProcessHandle.current().pid();
+ }
+
+ private String getHostName() {
+ try {
+ return InetAddress.getLocalHost().getHostName();
+ } catch (UnknownHostException e) {
+ return ""; //$NON-NLS-1$
+ }
+ }
+
+ @Override
+ public void close() {
+ boolean wasLocked = false;
+ try {
+ if (lock != null) {
+ lock.release();
+ wasLocked = true;
+ }
+ if (channel != null) {
+ channel.close();
+ }
+ if (f != null) {
+ f.close();
+ }
+ if (token != null) {
+ token.close();
+ }
+ if (wasLocked) {
+ FileUtils.delete(pidFile.toFile(), FileUtils.RETRY);
+ }
+ } catch (IOException e) {
+ LOG.error(MessageFormat
+ .format(JGitText.get().closePidLockFailed, pidFile), e);
+ }
+ }
+
+ }
}