Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix temp file path memory leak #3817

Merged
merged 2 commits into from
Jan 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 14 additions & 12 deletions common/src/main/java/bisq/common/storage/FileManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@
import bisq.common.proto.persistable.PersistenceProtoResolver;
import bisq.common.util.Utilities;

import com.google.common.util.concurrent.CycleDetectingLockFactory;

import java.nio.file.Path;
import java.nio.file.Paths;

import java.io.File;
Expand All @@ -37,7 +36,6 @@
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;

import lombok.extern.slf4j.Slf4j;

Expand All @@ -50,7 +48,7 @@ public class FileManager<T extends PersistableEnvelope> {
private final Callable<Void> saveFileTask;
private final AtomicReference<T> nextWrite;
private final PersistenceProtoResolver persistenceProtoResolver;
private final ReentrantLock writeLock = CycleDetectingLockFactory.newInstance(CycleDetectingLockFactory.Policies.THROW).newReentrantLock("writeLock");
private Path usedTempFilePath;

///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
Expand Down Expand Up @@ -87,9 +85,9 @@ public FileManager(File dir, File storageFile, long delay, PersistenceProtoResol
}
return null;
};
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
UserThread.execute(FileManager.this::shutDown);
}, "FileManager.ShutDownHook"));
Runtime.getRuntime().addShutdownHook(new Thread(() ->
UserThread.execute(FileManager.this::shutDown), "FileManager.ShutDownHook")
);
}


Expand Down Expand Up @@ -203,29 +201,33 @@ private synchronized void saveToFile(T persistable, File dir, File storageFile)
if (!dir.exists() && !dir.mkdir())
log.warn("make dir failed");

tempFile = File.createTempFile("temp", null, dir);
tempFile = usedTempFilePath != null
? FileUtil.createNewFile(usedTempFilePath)
: File.createTempFile("temp", null, dir);
// Don't use a new temp file path each time, as that causes the delete-on-exit hook to leak memory:
tempFile.deleteOnExit();

fileOutputStream = new FileOutputStream(tempFile);

log.debug("Writing protobuffer class:{} to file:{}", persistable.getClass(), storageFile.getName());
writeLock.lock();
protoPersistable.writeDelimitedTo(fileOutputStream);

// Attempt to force the bits to hit the disk. In reality the OS or hard disk itself may still decide
// to not write through to physical media for at least a few seconds, but this is the best we can do.
fileOutputStream.flush();
fileOutputStream.getFD().sync();
writeLock.unlock();

// Close resources before replacing file with temp file because otherwise it causes problems on windows
// when rename temp file
fileOutputStream.close();

FileUtil.renameFile(tempFile, storageFile);
usedTempFilePath = tempFile.toPath();
} catch (Throwable t) {
// If an error occurred, don't attempt to reuse this path again, in case temp file cleanup fails.
usedTempFilePath = null;
log.error("Error at saveToFile, storageFile=" + storageFile.toString(), t);
} finally {
if (writeLock.isLocked())
writeLock.unlock();
if (tempFile != null && tempFile.exists()) {
log.warn("Temp file still exists after failed save. We will delete it now. storageFile=" + storageFile);
if (!tempFile.delete())
Expand Down
9 changes: 9 additions & 0 deletions common/src/main/java/bisq/common/storage/FileUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import org.apache.commons.io.FileUtils;

import java.nio.file.Path;
import java.nio.file.Paths;

import java.io.File;
Expand Down Expand Up @@ -185,4 +186,12 @@ public static void renameFile(File oldFile, File newFile) throws IOException {
public static void copyDirectory(File source, File destination) throws IOException {
FileUtils.copyDirectory(source, destination);
}

static File createNewFile(Path path) throws IOException {
File file = path.toFile();
if (!file.createNewFile()) {
throw new IOException("There already exists a file with path: " + path);
}
return file;
}
}