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);