Skip to content

Commit

Permalink
Cleanup old lock files regularly (#37)
Browse files Browse the repository at this point in the history
  • Loading branch information
shartte authored Jul 19, 2024
1 parent 4b7030b commit 426e2f7
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public Integer call() throws Exception {
try (var cacheManager = commonOptions.createCacheManager()) {
cacheManager.performMaintenance();
}
var lockManager = commonOptions.createLockManager();
lockManager.performMaintenance();

return 0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ public static class Version {
@Override
public Integer call() throws Exception {
try (var downloadManager = new DownloadManager();
var cacheManager = commonOptions.createCacheManager();
var lockManager = commonOptions.createLockManager()) {
var cacheManager = commonOptions.createCacheManager()) {
var lockManager = commonOptions.createLockManager();

var launcherInstallations = commonOptions.createLauncherInstallations();
var artifactManager = commonOptions.createArtifactManager(cacheManager, downloadManager, lockManager, launcherInstallations);
Expand Down
41 changes: 37 additions & 4 deletions src/main/java/net/neoforged/neoform/runtime/cli/LockManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class LockManager implements AutoCloseable {
public class LockManager {
private static final Logger LOG = Logger.create();

private final Path lockDirectory;
Expand All @@ -22,7 +27,6 @@ public class LockManager implements AutoCloseable {
public LockManager(Path lockDirectory) throws IOException {
Files.createDirectories(lockDirectory);
this.lockDirectory = lockDirectory;
Runtime.getRuntime().addShutdownHook(new Thread(this::close));
}

private Path getLockFile(String key) {
Expand Down Expand Up @@ -102,9 +106,38 @@ public Lock lock(String key) {
return new Lock(fileLock);
}

@Override
public void close() {
/**
* Removes old outdated lock files.
*/
public void performMaintenance() {
FileTime newestToDelete = FileTime.from(Instant.now().minus(24, ChronoUnit.HOURS));

var lockFilesDeleted = new AtomicInteger();
try (var stream = Files.list(lockDirectory)) {
stream.filter(f -> {
var filename = f.getFileName().toString();
return filename.startsWith("_") && filename.endsWith(".lock");
}).filter(f -> {
try {
var attributes = Files.readAttributes(f, BasicFileAttributes.class);
return attributes.isRegularFile()
&& attributes.lastModifiedTime().compareTo(newestToDelete) < 0;
} catch (IOException ignored) {
return false;
}
}).forEach(f -> {
try {
Files.delete(f);
lockFilesDeleted.incrementAndGet();
} catch (IOException ignored) {
}
});
} catch (IOException ignored) {
}

if (lockFilesDeleted.get() > 0) {
LOG.println(AnsiColor.MUTED + " Deleted " + lockFilesDeleted.get() + " outdated lock files");
}
}

public static class Lock implements AutoCloseable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ public final Integer call() throws Exception {

var launcherInstallations = commonOptions.createLauncherInstallations();

try (var lockManager = commonOptions.createLockManager();
var cacheManager = commonOptions.createCacheManager();
try (var cacheManager = commonOptions.createCacheManager();
var downloadManager = new DownloadManager()) {
var lockManager = commonOptions.createLockManager();
cacheManager.setDisabled(disableCache);
cacheManager.setAnalyzeMisses(analyzeCacheMisses);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package net.neoforged.neoform.runtime.cli;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

class LockManagerTest {

private static final int MIN_AGE_TO_DELETE = 24;

@TempDir
Path tempDir;

@Test
void testMaintenance() throws Exception {
touchFile("subdir/_totallyalock.lock", MIN_AGE_TO_DELETE);
touchDir("_totallynota.lock");
touchFile("_not_old_enough.lock", MIN_AGE_TO_DELETE - 1);
touchFile("_should_be_deleted.lock", MIN_AGE_TO_DELETE);
touchFile("doesntstartwithunderscore.lock", MIN_AGE_TO_DELETE);
touchFile("_doesnotendwithlock", MIN_AGE_TO_DELETE);

var lockManager = new LockManager(tempDir);
lockManager.performMaintenance();

// This asserts what was NOT deleted
assertThat(listRecursively()).containsOnly(
"doesntstartwithunderscore.lock",
"subdir/",
"subdir/_totallyalock.lock",
"_doesnotendwithlock",
"_not_old_enough.lock",
"_totallynota.lock/"
);
}

private List<String> listRecursively() throws IOException {
try (var stream = Files.walk(tempDir)) {
return stream.map(p -> {
var result = tempDir.relativize(p).toString().replace('\\', '/');
if (Files.isDirectory(p)) {
result += "/";
}
return result;
})
.filter(p -> !"/".equals(p)).toList();
}
}

private void touchDir(String relativePath) throws IOException {
var path = tempDir.resolve(relativePath);
Files.createDirectories(path);
}

private void touchFile(String relativePath, long ageInHours) throws IOException {
var path = tempDir.resolve(relativePath);
Files.createDirectories(path.getParent());
Files.writeString(path, "");
Files.setLastModifiedTime(path, FileTime.from(Instant.now().minus(ageInHours, ChronoUnit.HOURS)));
}
}

0 comments on commit 426e2f7

Please sign in to comment.