diff --git a/base/src/main/java/de/staropensource/sosengine/base/Engine.java b/base/src/main/java/de/staropensource/sosengine/base/Engine.java index 05231e7e..2bc2db92 100644 --- a/base/src/main/java/de/staropensource/sosengine/base/Engine.java +++ b/base/src/main/java/de/staropensource/sosengine/base/Engine.java @@ -355,6 +355,20 @@ public final class Engine implements SubsystemMainClass { * @since v1-alpha0 */ public synchronized void shutdown(@Range(from = 0, to = 255) int exitCode) { + if (shuttingDown) { + logger.error("Already shutting down"); + return; + } + + // Make engine single-threaded + Properties properties = new Properties(); + properties.setProperty("sosengine.base.optimizeLogging", "false"); + properties.setProperty("sosengine.base.optimizeEvents", "false"); + EngineConfiguration.getInstance().loadConfiguration(properties); + + // Flush log messages + Logger.flushLogMessages(); + logger.info("Shutting engine down"); shuttingDown = true; @@ -365,10 +379,18 @@ public final class Engine implements SubsystemMainClass { new InternalEngineShutdownEvent().callEvent(); logger.verb("Shutting down JVM with code " + exitCode); - Logger.flushLogMessages(); // Flush all log messages before exiting Runtime.getRuntime().exit(exitCode); } + /** + * Shuts the engine and JVM down. + * + * @since v1-alpha0 + */ + public void shutdown() { + shutdown(0); + } + /** * {@inheritDoc} */ @@ -393,13 +415,4 @@ public final class Engine implements SubsystemMainClass { public DependencyVector getDependencyVector() { return new DependencyVector("engine", StarOpenSourceVersioningSystem.class, EngineInformation.getVersioningString()); } - - /** - * Shuts the engine and JVM down. - * - * @since v1-alpha0 - */ - public synchronized void shutdown() { - shutdown(0); - } } diff --git a/base/src/main/java/de/staropensource/sosengine/base/EngineConfiguration.java b/base/src/main/java/de/staropensource/sosengine/base/EngineConfiguration.java index 1dd23d61..3613be16 100644 --- a/base/src/main/java/de/staropensource/sosengine/base/EngineConfiguration.java +++ b/base/src/main/java/de/staropensource/sosengine/base/EngineConfiguration.java @@ -298,7 +298,8 @@ public final class EngineConfiguration implements SubsystemConfiguration { // Loop through all properties for (String property : properties.stringPropertyNames()) { // Check if property name starts with group - if (!property.startsWith(group)) continue; + if (!property.startsWith(group)) + continue; // Skip to important stuff property = property.substring(group.length()); diff --git a/graphics/glfw/src/main/java/de/staropensource/sosengine/graphics/glfw/GlfwSubsystem.java b/graphics/glfw/src/main/java/de/staropensource/sosengine/graphics/glfw/GlfwSubsystem.java index 66a0266e..c6fe9519 100644 --- a/graphics/glfw/src/main/java/de/staropensource/sosengine/graphics/glfw/GlfwSubsystem.java +++ b/graphics/glfw/src/main/java/de/staropensource/sosengine/graphics/glfw/GlfwSubsystem.java @@ -20,18 +20,17 @@ package de.staropensource.sosengine.graphics.glfw; import de.staropensource.sosengine.base.annotations.EngineSubsystem; -import de.staropensource.sosengine.base.annotations.EventListener; import de.staropensource.sosengine.base.classes.SubsystemMainClass; import de.staropensource.sosengine.base.data.info.EngineInformation; import de.staropensource.sosengine.base.data.versioning.StarOpenSourceVersioningSystem; -import de.staropensource.sosengine.base.internal.events.InternalEngineShutdownEvent; import de.staropensource.sosengine.base.logging.LoggerInstance; import de.staropensource.sosengine.base.types.CodePart; import de.staropensource.sosengine.base.types.DependencyVector; -import de.staropensource.sosengine.base.types.EventPriority; import de.staropensource.sosengine.base.types.logging.LogIssuer; +import de.staropensource.sosengine.base.utility.Miscellaneous; import de.staropensource.sosengine.graphics.glfw.events.GraphicsErrorEvent; import de.staropensource.sosengine.graphics.glfw.exceptions.GlfwInitializationException; +import de.staropensource.sosengine.graphics.glfw.exceptions.NotOnMainThreadException; import lombok.Getter; import org.jetbrains.annotations.NotNull; import org.lwjgl.glfw.GLFWErrorCallback; @@ -70,6 +69,15 @@ public class GlfwSubsystem implements SubsystemMainClass { */ private final LoggerInstance logger = new LoggerInstance(new LogIssuer(getClass(), CodePart.ENGINE)); + /** + * The {@link GLFWErrorCallback} to use. + *
+ * Only declared publicly for freeing during engine shutdown. + * + * @since v1-alpha2 + */ + private final GLFWErrorCallback errorCallback = GLFWErrorCallback.create(new GraphicsErrorEvent()); + /** * Constructs this subsystem. * @@ -107,11 +115,16 @@ public class GlfwSubsystem implements SubsystemMainClass { * @since v1-alpha2 */ public void initializeGlfw() { - // Set error callback - try (GLFWErrorCallback errorCallback = GLFWErrorCallback.create(new GraphicsErrorEvent())) { - errorCallback.set(); + logger.verb("Initializing GLFW"); + + if (!Miscellaneous.onMainThread()) { + logger.crash("Unable to initialize GLFW on a non-main thread", new NotOnMainThreadException(), true); + return; } + // Set error callback + errorCallback.set(); + // Initialize GLFW if (!glfwInit()) throw new GlfwInitializationException(); @@ -125,19 +138,12 @@ public class GlfwSubsystem implements SubsystemMainClass { public void terminateGlfw() { logger.verb("Terminating GLFW"); - glfwTerminate(); - //noinspection DataFlowIssue,resource - glfwSetErrorCallback(null).free(); - } + if (!Miscellaneous.onMainThread()) { + logger.crash("Unable to terminate GLFW on a non-main thread. Did you call Engine#shutdown or Logger#crash from another thread?", new NotOnMainThreadException(), true); + return; + } - /** - * Called when the engine shuts down. - * - * @since v1-alpha2 - */ - @EventListener(event = InternalEngineShutdownEvent.class, priority = EventPriority.EXTREMELY_UNIMPORTANT) - private static void shutdownApiFromEvent() { - if (instance != null) - instance.terminateGlfw(); + glfwTerminate(); + errorCallback.free(); } } diff --git a/graphics/src/main/java/de/staropensource/sosengine/graphics/GraphicsSubsystem.java b/graphics/src/main/java/de/staropensource/sosengine/graphics/GraphicsSubsystem.java index 389d500a..023ce854 100644 --- a/graphics/src/main/java/de/staropensource/sosengine/graphics/GraphicsSubsystem.java +++ b/graphics/src/main/java/de/staropensource/sosengine/graphics/GraphicsSubsystem.java @@ -29,7 +29,6 @@ import de.staropensource.sosengine.base.internal.events.InternalEngineShutdownEv import de.staropensource.sosengine.base.logging.LoggerInstance; import de.staropensource.sosengine.base.types.CodePart; import de.staropensource.sosengine.base.types.DependencyVector; -import de.staropensource.sosengine.base.types.EventPriority; import de.staropensource.sosengine.base.types.logging.LogIssuer; import de.staropensource.sosengine.base.utility.ListFormatter; import de.staropensource.sosengine.base.utility.Miscellaneous; @@ -161,12 +160,17 @@ public final class GraphicsSubsystem implements SubsystemMainClass { * * @since v1-alpha0 */ - @EventListener(event = InternalEngineShutdownEvent.class, priority = EventPriority.EXTREMELY_UNIMPORTANT) - public static void shutdown() { + @EventListener(event = InternalEngineShutdownEvent.class) + private static void shutdown() { LoggerInstance logger = instance.logger; logger.verb("Shutting down"); - long shutdownTime = Miscellaneous.measureExecutionTime(() -> new GraphicsApiShutdownEvent().callEvent()); + long shutdownTime = Miscellaneous.measureExecutionTime(() -> { + new GraphicsApiShutdownEvent().callEvent(); + + if (instance.api != null) + instance.api.shutdownApi(); + }); logger.info("Shut down in " + shutdownTime + "ms"); } @@ -201,7 +205,7 @@ public final class GraphicsSubsystem implements SubsystemMainClass { // Check if registered apis are compatible for (String apiName : registeredApis.keySet()) if (registeredApis.get(apiName).isCompatible()) { - logger.diag("" + apiName + " is compatible"); + logger.diag(apiName + " is compatible"); compatibleApis.add(apiName); } else logger.diag(apiName + " is not compatible"); diff --git a/testapp/src/main/java/de/staropensource/sosengine/testapp/Main.java b/testapp/src/main/java/de/staropensource/sosengine/testapp/Main.java index 11f79574..b3e7125f 100644 --- a/testapp/src/main/java/de/staropensource/sosengine/testapp/Main.java +++ b/testapp/src/main/java/de/staropensource/sosengine/testapp/Main.java @@ -20,16 +20,20 @@ package de.staropensource.sosengine.testapp; import de.staropensource.sosengine.base.Engine; +import de.staropensource.sosengine.base.annotations.EventListener; +import de.staropensource.sosengine.base.events.ThrowableCatchEvent; import de.staropensource.sosengine.base.logging.LoggerInstance; import de.staropensource.sosengine.base.types.CodePart; import de.staropensource.sosengine.base.types.logging.LogIssuer; import de.staropensource.sosengine.base.types.vectors.Vec2i; +import de.staropensource.sosengine.base.utility.Miscellaneous; import de.staropensource.sosengine.graphics.GraphicsSubsystem; import de.staropensource.sosengine.graphics.classes.ApiMainClass; import de.staropensource.sosengine.graphics.classes.ApiManagementClass; import de.staropensource.sosengine.graphics.classes.Window; import lombok.Getter; import lombok.SneakyThrows; +import org.jetbrains.annotations.NotNull; /** * The initialization class for sos!engine's development application. @@ -78,7 +82,7 @@ public class Main { /** * The program's entrypoint. - * Calls {@code run()} after invocation. + * Calls {@link #run(String[])} after invocation. * * @see Main#run(String[]) * @param args program's command line arguments @@ -88,15 +92,15 @@ public class Main { } /** - * The program's entrypoint but not static. - * Here's where the "real magic" happens- + * The program's entrypoint except it's not static. + * Here's where the "real magic" happens. * * @see Main#main(String[]) * @param args program's command line arguments */ @SneakyThrows public void run(String[] args) { - try { + Miscellaneous.executeSafely(() -> { // Initialize sos!engine engine = new Engine(); @@ -124,13 +128,27 @@ public class Main { } // Sleep for five seconds - Thread.sleep(5000); + try { + Thread.sleep(5000); + } catch (InterruptedException exception) { + logger.crash("Was unable to sleep for 5000ms", exception); + } // Shutdown - Engine.getInstance().shutdown(0); - } catch (Throwable throwable) { - Thread.ofVirtual().start(() -> Engine.getInstance().shutdown(69)); - throw throwable; + Engine.getInstance().shutdown(); + }, "mainThread"); + } + + /** + * Crashes the application when a throwable + * is thrown in {@link #run(String[])}. + * + * @since v1-alpha2 + */ + @EventListener(event = ThrowableCatchEvent.class) + public static void onThrowable(@NotNull Throwable throwable, @NotNull String identifier) { + if (identifier.equals("mainThread")) { + instance.logger.crash("The main thread threw an exception", throwable); } } }