Fix potential deadlock when chunkload raises non-IO exception (#4861)

This commit is contained in:
tterrag 2018-07-09 16:45:50 -04:00 committed by LexManos
parent da888df463
commit a98db5bef2
3 changed files with 84 additions and 15 deletions

View file

@ -41,7 +41,7 @@ public class ChunkIOExecutor
private static final int PLAYERS_PER_THREAD = 50;
private static final Map<QueuedChunk, ChunkIOProvider> tasks = Maps.newConcurrentMap();
private static final ThreadPoolExecutor pool = new ThreadPoolExecutor(BASE_THREADS, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
private static final ThreadPoolExecutor pool = new ChunkIOThreadPoolExecutor(BASE_THREADS, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(),
new ThreadFactory()
{

View file

@ -26,7 +26,6 @@ import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.math.ChunkPos;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.world.ChunkDataEvent;
import net.minecraftforge.fml.common.FMLLog;
import java.io.IOException;
import java.util.concurrent.ConcurrentLinkedQueue;
@ -63,24 +62,29 @@ class ChunkIOProvider implements Runnable
{
synchronized(this)
{
Object[] data = null;
try
{
data = this.loader.loadChunk__Async(chunkInfo.world, chunkInfo.x, chunkInfo.z);
}
catch (IOException e)
{
FMLLog.log.error("Failed to load chunk async.", e);
}
Object[] data = null;
try
{
data = this.loader.loadChunk__Async(chunkInfo.world, chunkInfo.x, chunkInfo.z);
}
catch (IOException e)
{
throw new RuntimeException(e); // Allow exception to bubble up to afterExecute
}
if (data != null)
{
this.nbt = (NBTTagCompound)data[1];
this.chunk = (Chunk)data[0];
if (data != null)
{
this.nbt = (NBTTagCompound)data[1];
this.chunk = (Chunk)data[0];
}
}
finally
{
this.ran = true;
this.notifyAll();
}
this.ran = true;
this.notifyAll();
}
}
@ -132,4 +136,9 @@ class ChunkIOProvider implements Runnable
this.callbacks.clear();
}
public QueuedChunk getChunkInfo()
{
return chunkInfo;
}
}

View file

@ -0,0 +1,60 @@
/*
* Minecraft Forge
* Copyright (c) 2016.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.common.chunkio;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import net.minecraft.crash.CrashReportCategory;
import net.minecraftforge.fml.common.FMLLog;
class ChunkIOThreadPoolExecutor extends ThreadPoolExecutor {
public ChunkIOThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)
{
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
}
@Override
protected void afterExecute(Runnable r, Throwable t)
{
if (t != null)
{
try
{
FMLLog.log.error("Unhandled exception loading chunk:", t);
QueuedChunk queuedChunk = ((ChunkIOProvider) r).getChunkInfo();
FMLLog.log.error(queuedChunk);
FMLLog.log.error(CrashReportCategory.getCoordinateInfo(queuedChunk.x << 4, 64, queuedChunk.z << 4));
}
catch (Throwable t2)
{
FMLLog.log.error(t2);
}
finally
{
// Make absolutely sure that we do not leave any deadlock case
r.notifyAll();
}
}
}
}