Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 22 additions & 6 deletions chunky/src/java/se/llbit/chunky/main/Chunky.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import se.llbit.log.Log;
import se.llbit.log.Receiver;
import se.llbit.util.TaskTracker;
import se.llbit.util.concurrent.ChunkyThread;

import java.io.File;
import java.io.FileInputStream;
Expand All @@ -56,6 +57,7 @@
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
Expand Down Expand Up @@ -211,9 +213,8 @@ public static void main(final String[] args) {
System.exit(1);
}

int exitCode = 0;
if (cmdline.mode == CommandLineOptions.Mode.CLI_OPERATION) {
exitCode = cmdline.exitCode;
System.exit(cmdline.exitCode);
} else {
// Initialize the common thread pool.
getCommonThreads();
Expand All @@ -222,6 +223,14 @@ public static void main(final String[] args) {
chunky.headless = cmdline.mode == Mode.HEADLESS_RENDER || cmdline.mode == Mode.CREATE_SNAPSHOT;
chunky.loadPlugins();

Runtime.getRuntime().addShutdownHook(
new Thread(() -> { // intentionally not a ChunkyThread
// Within a shutdown hook we need to close quickly, otherwise we risk the user/OS escalating to KILL
chunky.shutdown(1, TimeUnit.SECONDS);
})
);

int exitCode = 0;
try {
switch (cmdline.mode) {
case HEADLESS_RENDER:
Expand All @@ -240,12 +249,19 @@ public static void main(final String[] args) {
Log.error("Unchecked exception caused Chunky to close.", t);
exitCode = 2;
}
}
if (exitCode != 0) {
chunky.shutdown(5, TimeUnit.SECONDS);
// Always exit, we've done all shutdown necessary and want to exit whether non-daemon threads exist or not.
// This should prevent hangs if threads aren't cooperating.
System.exit(exitCode);
}
}

private void shutdown(int timeout, TimeUnit unit) {
if (!ChunkyThread.interruptAndJoinAll(timeout, unit)) {
Log.error("Not all threads were joined before shutting down."); // FIXME: list all alive threads? ThreadGroups are annoying.
}
}

/**
* This can be used by plugins to load the default Minecraft textures.
*/
Expand Down Expand Up @@ -342,7 +358,7 @@ public void update() {
public static ForkJoinPool getCommonThreads() {
if (commonThreads == null) {
// use at least two threads to prevent deadlocks in some java versions (see #1631)
commonThreads = new ForkJoinPool(Math.max(PersistentSettings.getNumThreads(), 2));
commonThreads = ChunkyThread.addForkJoinPool(new ForkJoinPool(Math.max(PersistentSettings.getNumThreads(), 2)));
}
return commonThreads;
}
Expand All @@ -353,7 +369,7 @@ public static ForkJoinPool getCommonThreads() {
*/
public static void setCommonThreadsCount(int threads) {
ForkJoinPool t = getCommonThreads();
commonThreads = new ForkJoinPool(threads);
commonThreads = ChunkyThread.addForkJoinPool(new ForkJoinPool(threads));
t.shutdown();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import se.llbit.chunky.resources.BitmapImage;
import se.llbit.log.Log;
import se.llbit.math.ColorUtil;
import se.llbit.util.concurrent.ChunkyThread;
import se.llbit.util.TaskTracker;

import java.time.Duration;
Expand All @@ -52,7 +53,7 @@
* <p>All available final renderers are stored in {@code renderers} and preview renderers
* are stored in {@code previewRenderers}.
*/
public class DefaultRenderManager extends Thread implements RenderManager {
public class DefaultRenderManager extends ChunkyThread implements RenderManager {
/**
* Map containing all the final render {@code Renderer}s. The renderer corresponding to
* {@code getRendererName()} is used when a render is requested.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package se.llbit.chunky.renderer;

import se.llbit.log.Log;
import se.llbit.util.concurrent.ChunkyThread;

import java.util.ArrayList;
import java.util.Random;
Expand All @@ -39,7 +40,7 @@ public interface Factory {
RenderWorkerPool create(int threads, long seed);
}

public static class RenderWorker extends Thread {
public static class RenderWorker extends ChunkyThread {
private final RenderWorkerPool pool;

public final Random random;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import se.llbit.chunky.world.RegionPosition;
import se.llbit.chunky.world.World;
import se.llbit.log.Log;
import se.llbit.util.concurrent.ChunkyThread;
import se.llbit.util.TaskTracker;

import java.io.File;
Expand All @@ -41,7 +42,7 @@
*
* @author Jesper Öqvist <jesper@llbit.se>
*/
public class AsynchronousSceneManager extends Thread implements SceneManager {
public class AsynchronousSceneManager extends ChunkyThread implements SceneManager {

private final SynchronousSceneManager sceneManager;
private final LinkedBlockingQueue<Runnable> taskQueue;
Expand Down
3 changes: 2 additions & 1 deletion chunky/src/java/se/llbit/chunky/renderer/scene/Scene.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
import se.llbit.nbt.Tag;
import se.llbit.util.*;
import se.llbit.util.annotation.NotNull;
import se.llbit.util.concurrent.ChunkyThread;
import se.llbit.util.io.PositionalInputStream;
import se.llbit.util.io.ZipExport;
import se.llbit.util.mojangapi.MinecraftProfile;
Expand Down Expand Up @@ -858,7 +859,7 @@ public synchronized void loadChunks(TaskTracker taskTracker, World world, Map<Re
int[] cubeWorldBlocks = new int[16*16*16];
int[] cubeWaterBlocks = new int[16*16*16];

ExecutorService executor = Executors.newSingleThreadExecutor();
ExecutorService executor = ChunkyThread.addExecutorService(Executors::newSingleThreadExecutor);

ChunkData[] regionParsingDataArray = new ChunkData[MCRegion.CHUNKS_X * MCRegion.CHUNKS_Z];
ChunkData[] chunkLoadingDataArray = new ChunkData[MCRegion.CHUNKS_X * MCRegion.CHUNKS_Z];
Expand Down
3 changes: 2 additions & 1 deletion chunky/src/java/se/llbit/chunky/ui/ChunkMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import se.llbit.chunky.world.region.MCRegion;
import se.llbit.log.Log;
import se.llbit.math.*;
import se.llbit.util.concurrent.ChunkyThread;

import java.awt.*;
import java.io.File;
Expand Down Expand Up @@ -115,7 +116,7 @@ public class ChunkMap implements ChunkUpdateListener, ChunkViewListener, CameraV
private volatile boolean scheduledUpdate = false;
private volatile long lastRedraw = 0;
private Runnable onViewDragged = () -> {};
private ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
private final ScheduledExecutorService executor = ChunkyThread.addExecutorService(Executors::newSingleThreadScheduledExecutor);

private boolean shouldDrawPlayers = true;

Expand Down
5 changes: 4 additions & 1 deletion chunky/src/java/se/llbit/chunky/ui/ChunkyFx.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
import se.llbit.chunky.resources.SettingsDirectory;
import se.llbit.chunky.ui.controller.ChunkyFxController;
import se.llbit.fxutil.WindowPosition;
import se.llbit.log.ConsoleReceiver;
import se.llbit.log.Level;
import se.llbit.log.Log;

import java.io.File;
Expand Down Expand Up @@ -79,10 +81,11 @@ public class ChunkyFx extends Application {

@Override
public void stop() throws Exception {
// UI is closing so we need to replace the receivers
Log.setReceiver(ConsoleReceiver.INSTANCE, Level.INFO, Level.WARNING, Level.ERROR);
if(mainStage != null) {
PersistentSettings.setWindowPosition(new WindowPosition(mainStage));
}
System.exit(0);
}

public static void startChunkyUI(Chunky chunkyInstance) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import se.llbit.json.JsonParser;
import se.llbit.log.Log;
import se.llbit.util.MinecraftText;
import se.llbit.util.concurrent.ChunkyThread;

import java.awt.*;
import java.io.File;
Expand Down Expand Up @@ -451,7 +452,7 @@ public void populate(

private static class PackListItem {

private final static Executor PACK_PARSER_EXECUTOR = Executors.newSingleThreadExecutor();
private final static Executor PACK_PARSER_EXECUTOR = ChunkyThread.addExecutorService(Executors::newSingleThreadExecutor);

private static PackListItem DEFAULT = null;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import se.llbit.json.JsonObject;
import se.llbit.json.JsonParser;
import se.llbit.log.Log;
import se.llbit.util.concurrent.ChunkyThread;

import java.io.File;
import java.io.FileInputStream;
Expand Down Expand Up @@ -64,6 +65,8 @@ public class SceneChooserController implements Initializable {

private Stage stage;

private static final Executor loadExecutor = ChunkyThread.addExecutorService(Executors::newSingleThreadExecutor);

private ChunkyFxController controller;

private static final HashMap<FileTimeCache, SceneListItem> sceneListCache = new HashMap<>();
Expand Down Expand Up @@ -219,7 +222,6 @@ public void setStage(Stage stage) {

private void populateSceneTable(File sceneDir) {
this.sceneTbl.setPlaceholder(new Label("Loading scenes…"));
Executor loadExecutor = Executors.newSingleThreadExecutor();

loadExecutor.execute(() -> {
List<SceneListItem> scenes = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
*/
package se.llbit.chunky.world;

import se.llbit.util.concurrent.ChunkyThread;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
Expand All @@ -25,7 +27,7 @@
*
* @author Jesper Öqvist (jesper@llbit.se)
*/
public class ChunkTopographyUpdater extends Thread {
public class ChunkTopographyUpdater extends ChunkyThread {

private final Set<Chunk> queue = new HashSet<>();

Expand Down
3 changes: 2 additions & 1 deletion chunky/src/java/se/llbit/chunky/world/SkymapTexture.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import se.llbit.math.QuickMath;
import se.llbit.math.Ray;
import se.llbit.math.Vector4;
import se.llbit.util.concurrent.ChunkyThread;
import se.llbit.util.ImageTools;

/**
Expand All @@ -35,7 +36,7 @@
*/
public class SkymapTexture extends Texture {

class TexturePreprocessor extends Thread {
class TexturePreprocessor extends ChunkyThread {
private final int x0;
private final int x1;
private final int y0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@
import se.llbit.chunky.map.WorldMapLoader;
import se.llbit.chunky.renderer.ChunkViewListener;
import se.llbit.chunky.world.ChunkView;
import se.llbit.util.concurrent.ChunkyThread;

/**
* Monitors filesystem for changes to region files.
*
* @author Jesper Öqvist <jesper@llbit.se>
*/
public abstract class RegionChangeWatcher extends Thread implements ChunkViewListener {
public abstract class RegionChangeWatcher extends ChunkyThread implements ChunkViewListener {
protected final WorldMapLoader mapLoader;
protected final MapView mapView;
protected volatile ChunkView view = ChunkView.EMPTY;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import se.llbit.chunky.map.WorldMapLoader;
import se.llbit.chunky.world.*;
import se.llbit.log.Log;
import se.llbit.util.concurrent.ChunkyThread;
import se.llbit.util.Mutable;

/**
Expand All @@ -34,7 +35,7 @@
*
* @author Jesper Öqvist (jesper@llbit.se)
*/
public class RegionParser extends Thread {
public class RegionParser extends ChunkyThread {

private final WorldMapLoader mapLoader;
private final RegionQueue queue;
Expand Down
Loading
Loading