Optimize startup and fix shutdown logic

This commit is contained in:
JeremyStar™ 2024-07-23 19:32:37 +02:00
parent 2899ba2e8a
commit 86e85356ba
Signed by: JeremyStarTM
GPG key ID: E366BAEF67E4704D
5 changed files with 86 additions and 44 deletions

View file

@ -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);
}
}

View file

@ -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());

View file

@ -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.
* <p>
* 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();
}
}

View file

@ -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");

View file

@ -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);
}
}
}