diff --git a/core/src/main/java/org/bitcoinj/store/SPVBlockStore.java b/core/src/main/java/org/bitcoinj/store/SPVBlockStore.java index 53d2325c..de8c0078 100644 --- a/core/src/main/java/org/bitcoinj/store/SPVBlockStore.java +++ b/core/src/main/java/org/bitcoinj/store/SPVBlockStore.java @@ -17,26 +17,19 @@ package org.bitcoinj.store; import org.bitcoinj.core.*; -import org.bitcoinj.utils.Threading; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.bitcoinj.utils.*; +import org.slf4j.*; -import javax.annotation.Nullable; -import java.io.File; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.ByteBuffer; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.channels.FileLock; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.concurrent.locks.ReentrantLock; +import javax.annotation.*; +import java.io.*; +import java.nio.*; +import java.nio.channels.*; +import java.util.*; +import java.util.concurrent.locks.*; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Preconditions.*; + +// TODO: Lose the mmap in this class. There are too many platform bugs that require odd workarounds. /** * An SPVBlockStore holds a limited number of block headers in a memory mapped ring buffer. With such a store, you @@ -269,9 +262,12 @@ public class SPVBlockStore implements BlockStore { public void close() throws BlockStoreException { try { buffer.force(); + if (System.getProperty("os.name").toLowerCase().contains("win")) { + log.info("Windows mmap hack: Forcing buffer cleaning"); + WindowsMMapHack.forceRelease(buffer); + } buffer = null; // Allow it to be GCd and the underlying file mapping to go away. randomAccessFile.close(); - fileLock.release(); } catch (IOException e) { throw new BlockStoreException(e); } diff --git a/core/src/main/java/org/bitcoinj/store/WindowsMMapHack.java b/core/src/main/java/org/bitcoinj/store/WindowsMMapHack.java new file mode 100644 index 00000000..d78aee58 --- /dev/null +++ b/core/src/main/java/org/bitcoinj/store/WindowsMMapHack.java @@ -0,0 +1,23 @@ +package org.bitcoinj.store; + +import sun.misc.*; +import sun.nio.ch.*; + +import java.nio.*; + +/** + *

This class knows how to force an mmap'd ByteBuffer to reliquish its file handles before it becomes garbage collected, + * by exploiting implementation details of the HotSpot JVM implementation.

+ * + *

This is required on Windows because otherwise an attempt to delete a file that is still mmapped will fail. This can + * happen when a user requests a "restore from seed" function, which involves deleting and recreating the chain file. + * At some point we should stop using mmap in SPVBlockStore and we can then delete this class.

+ * + *

It is a separate class to avoid hitting unknown imports when running on other JVMs.

+ */ +public class WindowsMMapHack { + public static void forceRelease(MappedByteBuffer buffer) { + Cleaner cleaner = ((DirectBuffer) buffer).cleaner(); + if (cleaner != null) cleaner.clean(); + } +}