diff --git a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java index a3936a4c3..594f4a31b 100755 --- a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java +++ b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java @@ -154,11 +154,11 @@ public final class ChronicleMapBuilder implements private static int MAX_BOOTSTRAPPING_HEADER_SIZE = (int) MemoryUnit.KILOBYTES.toBytes(16); - private static final ConcurrentHashMap - concurrentPersistedChronicleMapCreationControl = new ConcurrentHashMap<>(128); + private static final ConcurrentHashMap fileLockingControl = + new ConcurrentHashMap<>(128); - interface CreateMap { - ChronicleMap createMap() throws IOException; + interface FileIOAction { + void fileIOAction() throws IOException; } /** @@ -169,21 +169,21 @@ public final class ChronicleMapBuilder implements * persisted to the same File should be synchronized across JVM's threads. Simple way would be * to synchronize on some static (lock) object, but would serialize all Chronicle Maps creations * (persisted to any files), ConcurrentHashMap#compute() gives more scalability. - * ConcurrentHashMap is used effectively for lock striping only, the entries are removed - * immediately after compute() returns. + * ConcurrentHashMap is used effectively for lock striping only, because the entries are not + * even landing the map, because compute() always returns null. */ - private static ChronicleMap createMapFileSynchronized( - File file, CreateMap createMap) throws IOException { - ChronicleMap map = concurrentPersistedChronicleMapCreationControl.compute(file, (k, v) -> { + private static void fileLockedIO( + File file, FileChannel fileChannel, FileIOAction fileIOAction) throws IOException { + fileLockingControl.compute(file, (k, v) -> { try { - return createMap.createMap(); + try (FileLock ignored = fileChannel.lock()) { + fileIOAction.fileIOAction(); + } + return null; } catch (IOException e) { throw Jvm.rethrow(e); } }); - concurrentPersistedChronicleMapCreationControl.remove(file); - //noinspection unchecked - return map; } // not final because of cloning @@ -1395,7 +1395,7 @@ public final class ChronicleMapBuilder implements // clone() to make this builder instance thread-safe, because createWithFile() method // computes some state based on configurations, but doesn't synchronize on configuration // changes. - return createMapFileSynchronized(file, () -> clone().createWithFile(file, false, false)); + return clone().createWithFile(file, false, false); } @Override @@ -1406,8 +1406,7 @@ public final class ChronicleMapBuilder implements @Override public ChronicleMap recoverPersistedTo(File file, boolean sameBuilderConfig) throws IOException { - return createMapFileSynchronized(file, - () -> clone().createWithFile(file, true, sameBuilderConfig)); + return clone().createWithFile(file, true, sameBuilderConfig); } @Override @@ -1435,23 +1434,25 @@ public final class ChronicleMapBuilder implements if (raf.length() > 0) return openWithExistingFile(file, raf, recover, overrideBuilderConfig); - VanillaChronicleMap map = null; - ByteBuffer headerBuffer = null; - boolean newFile; + @SuppressWarnings("unchecked") + VanillaChronicleMap[] map = new VanillaChronicleMap[1]; + ByteBuffer[] headerBuffer = new ByteBuffer[1]; + boolean[] newFile = new boolean[1]; FileChannel fileChannel = raf.getChannel(); - try (FileLock ignored = fileChannel.lock()) { - if (raf.length() == 0) { - map = newMap(); - headerBuffer = writeHeader(fileChannel, map); - newFile = true; - } else { - newFile = false; - } - } - if (newFile) { - int headerSize = headerBuffer.remaining(); - return createWithNewFile(map, file, raf, headerBuffer, headerSize); + fileLockedIO(file, fileChannel, () -> { + if (raf.length() == 0) { + map[0] = newMap(); + headerBuffer[0] = writeHeader(fileChannel, map[0]); + newFile[0] = true; + } else { + newFile[0] = false; + } + }); + + if (newFile[0]) { + int headerSize = headerBuffer[0].remaining(); + return createWithNewFile(map[0], file, raf, headerBuffer[0], headerSize); } else { return openWithExistingFile(file, raf, recover, overrideBuilderConfig); }