diff --git a/src/main/java/li/cil/oc2/common/bus/device/vm/item/HardDriveDevice.java b/src/main/java/li/cil/oc2/common/bus/device/vm/item/HardDriveDevice.java index c18e92c9..b97cba9b 100644 --- a/src/main/java/li/cil/oc2/common/bus/device/vm/item/HardDriveDevice.java +++ b/src/main/java/li/cil/oc2/common/bus/device/vm/item/HardDriveDevice.java @@ -43,13 +43,32 @@ public class HardDriveDevice extends AbstractBlockStorageDevice createBlockDevice() { blobHandle = BlobStorage.validateHandle(blobHandle); - if (AsyncConfig.SERVER.asyncStorageOperations.get()) { + boolean useAsync = false; + try { + if (AsyncConfig.SERVER != null) { + useAsync = AsyncConfig.SERVER.asyncStorageOperations.get(); + } + } catch (IllegalStateException e) { + LOGGER.trace("Config not loaded yet, using default async storage operations setting"); + } + + if (useAsync) { return BlobStorage.getOrOpenAsync(blobHandle) .thenApplyAsync(channel -> { try { - if (AsyncConfig.SERVER.enableSuperDebug.get()) { + boolean debug = false; + try { + if (AsyncConfig.SERVER != null) { + debug = AsyncConfig.SERVER.enableSuperDebug.get(); + } + } catch (IllegalStateException e) { + // Config not loaded yet, use default debug setting + } + + if (debug) { LOGGER.debug("Mapping buffer for blob: {}", blobHandle); } + final MappedByteBuffer buffer = channel.map(MapMode.READ_WRITE, 0, size); return ByteBufferBlockDevice.wrap(buffer, readonly); } catch (final IOException e) { diff --git a/src/main/java/li/cil/oc2/common/util/AsyncUtils.java b/src/main/java/li/cil/oc2/common/util/AsyncUtils.java index 7c39c592..6e065141 100644 --- a/src/main/java/li/cil/oc2/common/util/AsyncUtils.java +++ b/src/main/java/li/cil/oc2/common/util/AsyncUtils.java @@ -75,23 +75,47 @@ public final class AsyncUtils { * @return a CompletableFuture that will complete when the task finishes */ public static CompletableFuture runAsync(Supplier task, String description) { - if (AsyncConfig.SERVER.enableSuperDebug.get()) { - LOGGER.info("Starting async task: {}", description); - logStackTrace("Async task stack trace"); + if (ASYNC_EXECUTOR.isShutdown()) { + LOGGER.warn("Attempted to submit async task '{}' after executor was shut down", description); + CompletableFuture failedFuture = new CompletableFuture<>(); + failedFuture.completeExceptionally(new RejectedExecutionException("Executor has been shut down")); + return failedFuture; } - return CompletableFuture.supplyAsync(() -> { - try { - return task.get(); - } catch (Throwable t) { - LOGGER.error("Error in async task: " + description, t); - throw t; - } finally { - if (AsyncConfig.SERVER.enableSuperDebug.get()) { - LOGGER.info("Completed async task: {}", description); - } + try { + if (AsyncConfig.SERVER != null && AsyncConfig.SERVER.enableSuperDebug.get()) { + LOGGER.info("Starting async task: {}", description); + logStackTrace("Async task stack trace"); } - }, ASYNC_EXECUTOR); + } catch (IllegalStateException e) { + // Config not loaded yet, skip debug logging + LOGGER.trace("Config not loaded yet, skipping debug logging for: {}", description); + } + + try { + return CompletableFuture.supplyAsync(() -> { + try { + return task.get(); + } catch (Throwable t) { + LOGGER.error("Error in async task: " + description, t); + throw t; + } finally { + try { + if (AsyncConfig.SERVER != null && AsyncConfig.SERVER.enableSuperDebug.get()) { + LOGGER.info("Completed async task: {}", description); + } + } catch (IllegalStateException e) { + // Config not loaded yet, skip debug logging + LOGGER.trace("Config not loaded yet, skipping debug logging for: {}", description); + } + } + }, ASYNC_EXECUTOR); + } catch (RejectedExecutionException e) { + LOGGER.warn("Failed to submit async task '{}' - executor is shutting down", description, e); + CompletableFuture failedFuture = new CompletableFuture<>(); + failedFuture.completeExceptionally(e); + return failedFuture; + } } /** @@ -107,6 +131,15 @@ public final class AsyncUtils { return null; }, description); } + + /** + * Checks if the async executor has been shut down. + * + * @return true if the executor has been shut down, false otherwise + */ + public static boolean isShutdown() { + return ASYNC_EXECUTOR.isShutdown(); + } /** * Logs the current stack trace if super debug mode is enabled. @@ -114,14 +147,19 @@ public final class AsyncUtils { * @param message the message to log with the stack trace */ public static void logStackTrace(String message) { - if (AsyncConfig.SERVER.enableSuperDebug.get()) { - StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); - StringBuilder sb = new StringBuilder(message).append("\n"); - // Skip the first 2 elements (getStackTrace and this method) - for (int i = 2; i < Math.min(stackTrace.length, 10); i++) { - sb.append(" at ").append(stackTrace[i]).append("\n"); + try { + if (AsyncConfig.SERVER != null && AsyncConfig.SERVER.enableSuperDebug.get()) { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + StringBuilder sb = new StringBuilder(message).append("\n"); + // Skip the first 2 elements (getStackTrace and this method) + for (int i = 2; i < stackTrace.length; i++) { + sb.append("\tat ").append(stackTrace[i]).append("\n"); + } + LOGGER.info(sb.toString()); } - LOGGER.debug(sb.toString()); + } catch (IllegalStateException e) { + // Config not loaded yet, skip debug logging + LOGGER.trace("Config not loaded yet, skipping stack trace logging"); } }