Fix race-condition-induced dead lock on replay restart in 1.14+

MC clears it scheduled tasks on disconnect. If we don't wait for
that to have happened on the async sender, then we might loose
some of the initial handshake packets (resulting in a dead lock in
fabric's registry syncing code).
This commit is contained in:
Jonas Herzig
2019-06-21 22:06:04 +02:00
parent 25ec311c86
commit 4213d37c76
2 changed files with 40 additions and 19 deletions

View File

@@ -42,6 +42,8 @@ import net.minecraft.util.NonBlockingThreadExecutor;
//$$ import com.google.common.util.concurrent.ListenableFutureTask; //$$ import com.google.common.util.concurrent.ListenableFutureTask;
//$$ import net.minecraft.resources.FolderPack; //$$ import net.minecraft.resources.FolderPack;
//$$ import net.minecraftforge.eventbus.api.SubscribeEvent; //$$ import net.minecraftforge.eventbus.api.SubscribeEvent;
//$$ import java.util.Queue;
//$$ import java.util.concurrent.FutureTask;
//$$ //$$
//#if MC>=11300 //#if MC>=11300
//$$ import com.replaymod.core.versions.LangResourcePack; //$$ import com.replaymod.core.versions.LangResourcePack;
@@ -89,8 +91,9 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Queue; import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import static com.replaymod.core.versions.MCVer.*; import static com.replaymod.core.versions.MCVer.*;
@@ -385,16 +388,28 @@ public class ReplayMod implements
}); });
} }
public void runSync(Runnable runnable) { /**
* Execute the given runnable on the main client thread, returning only after it has been run (or after 30 seconds).
*/
public void runSync(Runnable runnable) throws InterruptedException, ExecutionException, TimeoutException {
//#if MC>=11400 //#if MC>=11400
if (mc.isOnThread()) { if (mc.isOnThread()) {
//#else
//$$ if (mc.isCallingFromMinecraftThread()) {
//#endif
runnable.run(); runnable.run();
} else { } else {
runLater(runnable); executor.executeFuture(() -> {
runnable.run();
return null;
}).get(30, TimeUnit.SECONDS);
} }
//#else
//$$ if (mc.isCallingFromMinecraftThread()) {
//$$ runnable.run();
//$$ } else {
//$$ FutureTask<Void> future = new FutureTask<>(runnable, null);
//$$ runLater(future);
//$$ future.get(30, TimeUnit.SECONDS);
//$$ }
//#endif
} }
/** /**

View File

@@ -35,6 +35,8 @@ import net.minecraft.network.ClientConnection;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
//#if MC>=11300 //#if MC>=11300
import com.replaymod.replay.mixin.EntityLivingBaseAccessor; import com.replaymod.replay.mixin.EntityLivingBaseAccessor;
@@ -162,18 +164,22 @@ public class ReplayHandler {
channel.close(); channel.close();
// Force re-creation of camera entity by unloading the previous world // Force re-creation of camera entity by unloading the previous world
ReplayMod.instance.runSync(() -> { try {
//#if MC>=11300 ReplayMod.instance.runSync(() -> {
mc.mouse.unlockCursor(); //#if MC>=11300
//#else mc.mouse.unlockCursor();
//$$ mc.setIngameNotInFocus(); //#else
//#endif //$$ mc.setIngameNotInFocus();
//#if MC>=11400 //#endif
mc.disconnect(); //#if MC>=11400
//#else mc.disconnect();
//$$ mc.loadWorld(null); //#else
//#endif //$$ mc.loadWorld(null);
}); //#endif
});
} catch (InterruptedException | ExecutionException | TimeoutException e) {
LOGGER.error("Failed to properly restart (shutdown) replay:", e);
}
restrictions = new Restrictions(); restrictions = new Restrictions();