diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java index a9704e36b..3f7c746c3 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java @@ -39,6 +39,8 @@ import java.net.URI; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.channels.OverlappingFileLockException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -46,6 +48,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; /** * A clipboard with disk backed storage. (lower memory + loads on crash) @@ -59,6 +62,7 @@ public class DiskOptimizedClipboard extends LinearClipboard { private static final int HEADER_SIZE = 27; // Current header size private static final int VERSION_1_HEADER_SIZE = 22; // Header size of "version 1" private static final int VERSION_2_HEADER_SIZE = 27; // Header size of "version 2" i.e. when NBT/entities could be saved + private static final Map LOCK_HOLDER_CACHE = new ConcurrentHashMap<>(); private final HashMap nbtMap; private final File file; @@ -301,7 +305,23 @@ public class DiskOptimizedClipboard extends LinearClipboard { private void init() throws IOException { if (this.fileChannel == null) { this.fileChannel = braf.getChannel(); - this.fileChannel.lock(); + try { + FileLock lock = this.fileChannel.lock(); + LOCK_HOLDER_CACHE.put(file.getName(), new LockHolder(lock)); + } catch (OverlappingFileLockException e) { + LockHolder existing = LOCK_HOLDER_CACHE.get(file.getName()); + if (existing != null) { + long ms = System.currentTimeMillis() - existing.lockHeldSince; + LOGGER.error( + "Cannot lock clipboard file {} acquired by thread {}, {}ms ago", + file.getName(), + existing.thread, + ms + ); + } + // Rethrow to prevent clipboard access + throw e; + } this.byteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, braf.length()); } } @@ -737,4 +757,18 @@ public class DiskOptimizedClipboard extends LinearClipboard { return false; } + private static class LockHolder { + + final FileLock lock; + final long lockHeldSince; + final String thread; + + LockHolder(FileLock lock) { + this.lock = lock; + lockHeldSince = System.currentTimeMillis(); + this.thread = Thread.currentThread().getName(); + } + } + + }