Fix livelock during netty write to embedded channel (fixes #85)
This commit is contained in:
@@ -8,9 +8,10 @@ import com.replaymod.replay.camera.CameraEntity;
|
|||||||
import com.replaymod.replaystudio.replay.ReplayFile;
|
import com.replaymod.replaystudio.replay.ReplayFile;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
|
import io.netty.channel.ChannelDuplexHandler;
|
||||||
import io.netty.channel.ChannelHandler.Sharable;
|
import io.netty.channel.ChannelHandler.Sharable;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
import io.netty.channel.ChannelPromise;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.gui.GuiDownloadTerrain;
|
import net.minecraft.client.gui.GuiDownloadTerrain;
|
||||||
import net.minecraft.client.gui.GuiErrorScreen;
|
import net.minecraft.client.gui.GuiErrorScreen;
|
||||||
@@ -37,7 +38,7 @@ import java.util.Map;
|
|||||||
* the replay restart from the beginning.
|
* the replay restart from the beginning.
|
||||||
*/
|
*/
|
||||||
@Sharable
|
@Sharable
|
||||||
public class ReplaySender extends ChannelInboundHandlerAdapter {
|
public class ReplaySender extends ChannelDuplexHandler {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Previously packets for the client player were inserted using one fixed entity id (this one).
|
* Previously packets for the client player were inserted using one fixed entity id (this one).
|
||||||
@@ -487,6 +488,25 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
|
|||||||
super.channelActive(ctx);
|
super.channelActive(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||||
|
// The embedded channel's event loop will consider every thread to be in it and as such provides no
|
||||||
|
// guarantees that only one thread is using the pipeline at any one time.
|
||||||
|
// For reading the replay sender (either sync or async) is the only thread ever writing.
|
||||||
|
// For writing it may very well happen that multiple threads want to use the pipline at the same time.
|
||||||
|
// It's unclear whether the EmbeddedChannel is supposed to be thread-safe (the behavior of the event loop
|
||||||
|
// does suggest that). However it seems like it either isn't (likely) or there is a race condition.
|
||||||
|
// See: https://www.replaymod.com/forum/thread/1752#post8045 (https://paste.replaymod.com/lotacatuwo)
|
||||||
|
// To work around this issue, we just outright drop all write/flush requests (they aren't needed anyway).
|
||||||
|
// This still leaves channel handlers upstream with the threading issue but they all seem to cope well with it.
|
||||||
|
promise.setSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void flush(ChannelHandlerContext ctx) throws Exception {
|
||||||
|
// See write method above
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the replay is currently paused.
|
* Whether the replay is currently paused.
|
||||||
* @return {@code true} if it is paused, {@code false} otherwise
|
* @return {@code true} if it is paused, {@code false} otherwise
|
||||||
|
|||||||
Reference in New Issue
Block a user