Remove stray files (probes or lock files) created by background threads
NOTE: port back from master branch. On process exit, it was possible that the filesystem timestamp resolution measurement left behind .probe files or even a lock file for the jgit.config. Ensure the SAVE_RUNNER is shut down when the process exits (via System.exit() or otherwise). Move lf.lock() into the try-finally block when saving the config file. Delete .probe files on JVM shutdown -- they are created in daemon threads that may terminate abruptly, not executing the "finally" clause that normally removes these files. Bug: 579445 Change-Id: Iaee2301eb14e6201406398a90228ad10cfea6098
This commit is contained in:
parent
78c9b9260a
commit
d67ac798f1
|
@ -200,4 +200,16 @@ public void testLockForAppend() throws Exception {
|
||||||
assertFalse(lock.isLocked());
|
assertFalse(lock.isLocked());
|
||||||
checkFile(f, "contentother");
|
checkFile(f, "contentother");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnlockNoop() throws Exception {
|
||||||
|
File f = writeTrashFile("somefile", "content");
|
||||||
|
try {
|
||||||
|
LockFile lock = new LockFile(f);
|
||||||
|
lock.unlock();
|
||||||
|
lock.unlock();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
fail("unlock should be noop if not locked at all.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -216,9 +216,10 @@ public void save() throws IOException {
|
||||||
}
|
}
|
||||||
|
|
||||||
final LockFile lf = new LockFile(getFile());
|
final LockFile lf = new LockFile(getFile());
|
||||||
if (!lf.lock())
|
|
||||||
throw new LockFailedException(getFile());
|
|
||||||
try {
|
try {
|
||||||
|
if (!lf.lock()) {
|
||||||
|
throw new LockFailedException(getFile());
|
||||||
|
}
|
||||||
lf.setNeedSnapshotNoConfig(true);
|
lf.setNeedSnapshotNoConfig(true);
|
||||||
lf.write(out);
|
lf.write(out);
|
||||||
if (!lf.commit())
|
if (!lf.commit())
|
||||||
|
|
|
@ -48,7 +48,6 @@
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
@ -263,8 +262,9 @@ public static final class FileStoreAttributes {
|
||||||
*
|
*
|
||||||
* @see java.util.concurrent.Executors#newCachedThreadPool()
|
* @see java.util.concurrent.Executors#newCachedThreadPool()
|
||||||
*/
|
*/
|
||||||
private static final Executor FUTURE_RUNNER = new ThreadPoolExecutor(0,
|
private static final ExecutorService FUTURE_RUNNER = new ThreadPoolExecutor(
|
||||||
5, 30L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
|
0, 5, 30L, TimeUnit.SECONDS,
|
||||||
|
new LinkedBlockingQueue<Runnable>(),
|
||||||
runnable -> {
|
runnable -> {
|
||||||
Thread t = new Thread(runnable,
|
Thread t = new Thread(runnable,
|
||||||
"JGit-FileStoreAttributeReader-" //$NON-NLS-1$
|
"JGit-FileStoreAttributeReader-" //$NON-NLS-1$
|
||||||
|
@ -286,8 +286,9 @@ public static final class FileStoreAttributes {
|
||||||
* small keep-alive time to avoid delays on shut-down.
|
* small keep-alive time to avoid delays on shut-down.
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
private static final Executor SAVE_RUNNER = new ThreadPoolExecutor(0, 1,
|
private static final ExecutorService SAVE_RUNNER = new ThreadPoolExecutor(
|
||||||
1L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(),
|
0, 1, 1L, TimeUnit.MILLISECONDS,
|
||||||
|
new LinkedBlockingQueue<Runnable>(),
|
||||||
runnable -> {
|
runnable -> {
|
||||||
Thread t = new Thread(runnable,
|
Thread t = new Thread(runnable,
|
||||||
"JGit-FileStoreAttributeWriter-" //$NON-NLS-1$
|
"JGit-FileStoreAttributeWriter-" //$NON-NLS-1$
|
||||||
|
@ -297,6 +298,18 @@ public static final class FileStoreAttributes {
|
||||||
return t;
|
return t;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
static {
|
||||||
|
// Shut down the SAVE_RUNNER on System.exit()
|
||||||
|
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||||
|
try {
|
||||||
|
SAVE_RUNNER.shutdownNow();
|
||||||
|
SAVE_RUNNER.awaitTermination(100, TimeUnit.MILLISECONDS);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Ignore; we're shutting down
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether FileStore attributes should be determined asynchronously
|
* Whether FileStore attributes should be determined asynchronously
|
||||||
*
|
*
|
||||||
|
@ -453,11 +466,13 @@ private static FileStoreAttributes getFileStoreAttributes(Path dir) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// fall through and return fallback
|
// fall through and return fallback
|
||||||
} catch (IOException | InterruptedException
|
} catch (IOException | ExecutionException | CancellationException e) {
|
||||||
| ExecutionException | CancellationException e) {
|
|
||||||
LOG.error(e.getMessage(), e);
|
LOG.error(e.getMessage(), e);
|
||||||
} catch (TimeoutException | SecurityException e) {
|
} catch (TimeoutException | SecurityException e) {
|
||||||
// use fallback
|
// use fallback
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LOG.error(e.getMessage(), e);
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
}
|
}
|
||||||
LOG.debug("{}: use fallback timestamp resolution for directory {}", //$NON-NLS-1$
|
LOG.debug("{}: use fallback timestamp resolution for directory {}", //$NON-NLS-1$
|
||||||
Thread.currentThread(), dir);
|
Thread.currentThread(), dir);
|
||||||
|
@ -475,6 +490,7 @@ private static Duration measureMinimalRacyInterval(Path dir) {
|
||||||
Path probe = dir.resolve(".probe-" + UUID.randomUUID()); //$NON-NLS-1$
|
Path probe = dir.resolve(".probe-" + UUID.randomUUID()); //$NON-NLS-1$
|
||||||
Instant end = Instant.now().plusSeconds(3);
|
Instant end = Instant.now().plusSeconds(3);
|
||||||
try {
|
try {
|
||||||
|
probe.toFile().deleteOnExit();
|
||||||
Files.createFile(probe);
|
Files.createFile(probe);
|
||||||
do {
|
do {
|
||||||
n++;
|
n++;
|
||||||
|
@ -541,6 +557,7 @@ private static Optional<Duration> measureFsTimestampResolution(
|
||||||
}
|
}
|
||||||
Path probe = dir.resolve(".probe-" + UUID.randomUUID()); //$NON-NLS-1$
|
Path probe = dir.resolve(".probe-" + UUID.randomUUID()); //$NON-NLS-1$
|
||||||
try {
|
try {
|
||||||
|
probe.toFile().deleteOnExit();
|
||||||
Files.createFile(probe);
|
Files.createFile(probe);
|
||||||
Duration fsResolution = getFsResolution(s, dir, probe);
|
Duration fsResolution = getFsResolution(s, dir, probe);
|
||||||
Duration clockResolution = measureClockResolution();
|
Duration clockResolution = measureClockResolution();
|
||||||
|
|
Loading…
Reference in New Issue