diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/bugfix/buffer_builder_leak/BufferBuilderMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/bugfix/buffer_builder_leak/BufferBuilderMixin.java index 3a6e190b..98237da3 100644 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/bugfix/buffer_builder_leak/BufferBuilderMixin.java +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/bugfix/buffer_builder_leak/BufferBuilderMixin.java @@ -2,9 +2,12 @@ package org.embeddedt.modernfix.common.mixin.bugfix.buffer_builder_leak; import com.mojang.blaze3d.vertex.BufferBuilder; import org.embeddedt.modernfix.ModernFix; -import org.lwjgl.system.MemoryUtil; +import org.embeddedt.modernfix.render.UnsafeBufferHelper; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.nio.ByteBuffer; @@ -12,9 +15,16 @@ import java.nio.ByteBuffer; public class BufferBuilderMixin { @Shadow private ByteBuffer buffer; - private static final MemoryUtil.MemoryAllocator ALLOCATOR = MemoryUtil.getAllocator(false); private static boolean leakReported = false; + /** + * Ensure UnsafeBufferHelper is classloaded early, to avoid Forge's event transformer showing an error in the log. + */ + @Inject(method = "", at = @At(value = "RETURN")) + private static void initUnsafeBufferHelper(CallbackInfo ci) { + UnsafeBufferHelper.init(); + } + @Override protected void finalize() throws Throwable { try { @@ -25,7 +35,7 @@ public class BufferBuilderMixin { leakReported = true; ModernFix.LOGGER.warn("One or more BufferBuilders have been leaked, ModernFix will attempt to correct this."); } - ALLOCATOR.free(MemoryUtil.memAddress0(buf)); + UnsafeBufferHelper.free(buf); buffer = null; } } finally { diff --git a/common/src/main/java/org/embeddedt/modernfix/render/UnsafeBufferHelper.java b/common/src/main/java/org/embeddedt/modernfix/render/UnsafeBufferHelper.java new file mode 100644 index 00000000..69ab0cfe --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/render/UnsafeBufferHelper.java @@ -0,0 +1,51 @@ +package org.embeddedt.modernfix.render; + +import org.embeddedt.modernfix.ModernFix; +import org.lwjgl.system.MemoryUtil; +import sun.misc.Unsafe; + +import java.lang.reflect.Field; +import java.nio.ByteBuffer; + +/** + * Helper that frees ByteBuffers allocated by BufferBuilders, and nulls out the address pointer + * to prevent double frees. + * + * @author Moulberry + */ +public class UnsafeBufferHelper { + private static final MemoryUtil.MemoryAllocator ALLOCATOR = MemoryUtil.getAllocator(false); + + private static sun.misc.Unsafe UNSAFE = null; + private static long ADDRESS = -1; + + static { + try { + final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); + theUnsafe.setAccessible(true); + UNSAFE = (Unsafe)theUnsafe.get(null); + + final Field addressField = MemoryUtil.class.getDeclaredField("ADDRESS"); + addressField.setAccessible(true); + ADDRESS = addressField.getLong(null); + } catch(Throwable t) { + ModernFix.LOGGER.error("Could load unsafe/buffer address", t); + } + } + + public static void init() { + + } + + public static void free(ByteBuffer buf) { + if(UNSAFE != null && ADDRESS >= 0) { + // set the address to 0 to prevent double free + long address = UNSAFE.getAndSetLong(buf, ADDRESS, 0); + if(address != 0) { + ALLOCATOR.free(address); + } + } else { + ALLOCATOR.free(MemoryUtil.memAddress0(buf)); + } + } +}