diff --git a/src/main/java/net/openhft/chronicle/hash/impl/VanillaChronicleHash.java b/src/main/java/net/openhft/chronicle/hash/impl/VanillaChronicleHash.java
index eb39bf4b5..a1345c874 100644
--- a/src/main/java/net/openhft/chronicle/hash/impl/VanillaChronicleHash.java
+++ b/src/main/java/net/openhft/chronicle/hash/impl/VanillaChronicleHash.java
@@ -24,6 +24,7 @@ import net.openhft.chronicle.core.Maths;
import net.openhft.chronicle.core.OS;
import net.openhft.chronicle.hash.*;
import net.openhft.chronicle.hash.impl.util.BuildVersion;
+import net.openhft.chronicle.hash.impl.util.CanonicalRandomAccessFiles;
import net.openhft.chronicle.hash.impl.util.jna.PosixMsync;
import net.openhft.chronicle.hash.impl.util.jna.WindowsMsync;
import net.openhft.chronicle.hash.serialization.DataAccess;
@@ -38,6 +39,7 @@ import net.openhft.chronicle.wire.WireOut;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import sun.misc.Cleaner;
import java.io.File;
import java.io.IOException;
@@ -126,6 +128,8 @@ public abstract class VanillaChronicleHash.
+ */
+
+package net.openhft.chronicle.hash.impl.util;
+
+import net.openhft.chronicle.core.Jvm;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.concurrent.ConcurrentHashMap;
+
+public final class CanonicalRandomAccessFiles {
+
+ private static class RafReference {
+ RandomAccessFile raf;
+ int refCount;
+
+ RafReference(RandomAccessFile raf) {
+ this.raf = raf;
+ refCount = 1;
+ }
+ }
+
+ private static final ConcurrentHashMap canonicalRafs =
+ new ConcurrentHashMap<>();
+
+ public static RandomAccessFile acquire(File file) throws FileNotFoundException {
+ return canonicalRafs.compute(file, (f, ref) -> {
+ if (ref == null) {
+ try {
+ return new RafReference(new RandomAccessFile(f, "rw"));
+ } catch (FileNotFoundException e) {
+ throw Jvm.rethrow(e);
+ }
+ } else {
+ ref.refCount++;
+ return ref;
+ }
+ }).raf;
+ }
+
+ public static void release(File file) throws IOException {
+ canonicalRafs.compute(file, (f, ref) -> {
+ if (ref == null)
+ throw new IllegalStateException("releasing not referenced RAF of file " + f);
+ if (--ref.refCount == 0) {
+ try {
+ ref.raf.close();
+ } catch (IOException e) {
+ throw Jvm.rethrow(e);
+ }
+ return null;
+ } else {
+ return ref;
+ }
+ });
+ }
+}
diff --git a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java
index 594f4a31b..be300e71d 100755
--- a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java
+++ b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java
@@ -29,6 +29,7 @@ import net.openhft.chronicle.hash.impl.CompactOffHeapLinearHashTable;
import net.openhft.chronicle.hash.impl.SizePrefixedBlob;
import net.openhft.chronicle.hash.impl.VanillaChronicleHash;
import net.openhft.chronicle.hash.impl.stage.entry.ChecksumStrategy;
+import net.openhft.chronicle.hash.impl.util.CanonicalRandomAccessFiles;
import net.openhft.chronicle.hash.impl.util.math.PoissonDistribution;
import net.openhft.chronicle.hash.serialization.*;
import net.openhft.chronicle.hash.serialization.impl.SerializationBuilder;
@@ -1430,9 +1431,11 @@ public final class ChronicleMapBuilder implements
//noinspection ResultOfMethodCallIgnored
file.createNewFile();
}
- try (RandomAccessFile raf = new RandomAccessFile(file, "rw")) {
+ ChronicleMap result = null;
+ RandomAccessFile raf = CanonicalRandomAccessFiles.acquire(file);
+ try {
if (raf.length() > 0)
- return openWithExistingFile(file, raf, recover, overrideBuilderConfig);
+ return result = openWithExistingFile(file, raf, recover, overrideBuilderConfig);
@SuppressWarnings("unchecked")
VanillaChronicleMap[] map = new VanillaChronicleMap[1];
@@ -1452,10 +1455,13 @@ public final class ChronicleMapBuilder implements
if (newFile[0]) {
int headerSize = headerBuffer[0].remaining();
- return createWithNewFile(map[0], file, raf, headerBuffer[0], headerSize);
+ return result = createWithNewFile(map[0], file, raf, headerBuffer[0], headerSize);
} else {
- return openWithExistingFile(file, raf, recover, overrideBuilderConfig);
+ return result = openWithExistingFile(file, raf, recover, overrideBuilderConfig);
}
+ } finally {
+ if (result == null)
+ CanonicalRandomAccessFiles.release(file);
}
}
@@ -1668,7 +1674,7 @@ public final class ChronicleMapBuilder implements
throws IOException {
FileChannel fileChannel = raf.getChannel();
// see HCOLL-396
- map.msync(raf);
+ map.msync();
//noinspection PointlessBitwiseExpression
headerBuffer.putInt(SIZE_WORD_OFFSET, READY | DATA | headerSize);