1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-06-30 09:02:59 +00:00

Move MainThread config to its own class

This means the config is no longer stored as static fields, which is a
little cleaner. Would like to move everything else in the future, but
this is a good first step.
This commit is contained in:
Jonathan Coates 2023-05-31 22:20:18 +01:00
parent 590ef86c93
commit 5082b1677c
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
12 changed files with 93 additions and 33 deletions

View File

@ -12,6 +12,7 @@ import dan200.computercraft.core.ComputerContext;
import dan200.computercraft.core.computer.ComputerThread; import dan200.computercraft.core.computer.ComputerThread;
import dan200.computercraft.core.computer.GlobalEnvironment; import dan200.computercraft.core.computer.GlobalEnvironment;
import dan200.computercraft.core.computer.mainthread.MainThread; import dan200.computercraft.core.computer.mainthread.MainThread;
import dan200.computercraft.core.computer.mainthread.MainThreadConfig;
import dan200.computercraft.core.lua.CobaltLuaMachine; import dan200.computercraft.core.lua.CobaltLuaMachine;
import dan200.computercraft.core.lua.ILuaMachine; import dan200.computercraft.core.lua.ILuaMachine;
import dan200.computercraft.impl.AbstractComputerCraftAPI; import dan200.computercraft.impl.AbstractComputerCraftAPI;
@ -65,7 +66,7 @@ public final class ServerContext {
private ServerContext(MinecraftServer server) { private ServerContext(MinecraftServer server) {
this.server = server; this.server = server;
storageDir = server.getWorldPath(FOLDER); storageDir = server.getWorldPath(FOLDER);
mainThread = new MainThread(); mainThread = new MainThread(mainThreadConfig);
context = new ComputerContext( context = new ComputerContext(
new Environment(server), new Environment(server),
new ComputerThread(ConfigSpec.computerThreads.get()), new ComputerThread(ConfigSpec.computerThreads.get()),
@ -215,4 +216,16 @@ public final class ServerContext {
return ComputerCraftAPI.MOD_ID + "/" + ComputerCraftAPI.getInstalledVersion(); return ComputerCraftAPI.MOD_ID + "/" + ComputerCraftAPI.getInstalledVersion();
} }
} }
private static final MainThreadConfig mainThreadConfig = new MainThreadConfig() {
@Override
public long maxGlobalTime() {
return TimeUnit.MILLISECONDS.toNanos(ConfigSpec.maxMainGlobalTime.get());
}
@Override
public long maxComputerTime() {
return TimeUnit.MILLISECONDS.toNanos(ConfigSpec.maxMainComputerTime.get());
}
};
} }

View File

@ -9,6 +9,7 @@ import dan200.computercraft.core.CoreConfig;
import dan200.computercraft.core.Logging; import dan200.computercraft.core.Logging;
import dan200.computercraft.core.apis.http.NetworkUtils; import dan200.computercraft.core.apis.http.NetworkUtils;
import dan200.computercraft.core.apis.http.options.Action; import dan200.computercraft.core.apis.http.options.Action;
import dan200.computercraft.core.computer.mainthread.MainThreadConfig;
import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer; import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer;
import dan200.computercraft.shared.platform.PlatformHelper; import dan200.computercraft.shared.platform.PlatformHelper;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
@ -155,14 +156,14 @@ public final class ConfigSpec {
milliseconds. milliseconds.
Note, we will quite possibly go over this limit, as there's no way to tell how Note, we will quite possibly go over this limit, as there's no way to tell how
long a will take - this aims to be the upper bound of the average time.""") long a will take - this aims to be the upper bound of the average time.""")
.defineInRange("max_main_global_time", (int) TimeUnit.NANOSECONDS.toMillis(CoreConfig.maxMainGlobalTime), 1, Integer.MAX_VALUE); .defineInRange("max_main_global_time", (int) TimeUnit.NANOSECONDS.toMillis(MainThreadConfig.DEFAULT_MAX_GLOBAL_TIME), 1, Integer.MAX_VALUE);
maxMainComputerTime = builder maxMainComputerTime = builder
.comment(""" .comment("""
The ideal maximum time a computer can execute for in a tick, in milliseconds. The ideal maximum time a computer can execute for in a tick, in milliseconds.
Note, we will quite possibly go over this limit, as there's no way to tell how Note, we will quite possibly go over this limit, as there's no way to tell how
long a will take - this aims to be the upper bound of the average time.""") long a will take - this aims to be the upper bound of the average time.""")
.defineInRange("max_main_computer_time", (int) TimeUnit.NANOSECONDS.toMillis(CoreConfig.maxMainComputerTime), 1, Integer.MAX_VALUE); .defineInRange("max_main_computer_time", (int) TimeUnit.NANOSECONDS.toMillis(MainThreadConfig.DEFAULT_MAX_COMPUTER_TIME), 1, Integer.MAX_VALUE);
builder.pop(); builder.pop();
} }
@ -348,10 +349,6 @@ public final class ConfigSpec {
CoreConfig.defaultComputerSettings = defaultComputerSettings.get(); CoreConfig.defaultComputerSettings = defaultComputerSettings.get();
Config.commandRequireCreative = commandRequireCreative.get(); Config.commandRequireCreative = commandRequireCreative.get();
// Execution
CoreConfig.maxMainGlobalTime = TimeUnit.MILLISECONDS.toNanos(maxMainGlobalTime.get());
CoreConfig.maxMainComputerTime = TimeUnit.MILLISECONDS.toNanos(maxMainComputerTime.get());
// Update our log filter if needed. // Update our log filter if needed.
var logFilter = MarkerFilter.createFilter( var logFilter = MarkerFilter.createFilter(
Logging.COMPUTER_ERROR.getName(), Logging.COMPUTER_ERROR.getName(),

View File

@ -9,7 +9,6 @@ import dan200.computercraft.core.apis.http.options.AddressRule;
import java.util.List; import java.util.List;
import java.util.OptionalInt; import java.util.OptionalInt;
import java.util.concurrent.TimeUnit;
/** /**
* Config options for ComputerCraft's Lua runtime. * Config options for ComputerCraft's Lua runtime.
@ -25,9 +24,6 @@ public final class CoreConfig {
public static boolean disableLua51Features = false; public static boolean disableLua51Features = false;
public static String defaultComputerSettings = ""; public static String defaultComputerSettings = "";
public static long maxMainGlobalTime = TimeUnit.MILLISECONDS.toNanos(10);
public static long maxMainComputerTime = TimeUnit.MILLISECONDS.toNanos(5);
public static boolean httpEnabled = true; public static boolean httpEnabled = true;
public static boolean httpWebsocketEnabled = true; public static boolean httpWebsocketEnabled = true;
public static List<AddressRule> httpRules = List.of( public static List<AddressRule> httpRules = List.of(

View File

@ -18,7 +18,6 @@ import java.util.Map;
/** /**
* Implementation of {@link IComputerAccess}/{@link IComputerSystem} for usage by externally registered APIs. * Implementation of {@link IComputerAccess}/{@link IComputerSystem} for usage by externally registered APIs.
* *
* @see dan200.computercraft.api.ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
* @see ILuaAPIFactory * @see ILuaAPIFactory
* @see ApiWrapper * @see ApiWrapper
*/ */

View File

@ -4,7 +4,6 @@
package dan200.computercraft.core.computer.mainthread; package dan200.computercraft.core.computer.mainthread;
import dan200.computercraft.core.CoreConfig;
import dan200.computercraft.core.metrics.MetricsObserver; import dan200.computercraft.core.metrics.MetricsObserver;
import java.util.HashSet; import java.util.HashSet;
@ -20,7 +19,7 @@ import java.util.TreeSet;
* {@link MainThread} starts cool, and runs as many tasks as it can in the current {@link #budget}ns. Any external tasks * {@link MainThread} starts cool, and runs as many tasks as it can in the current {@link #budget}ns. Any external tasks
* (those run by tile entities, etc...) will also consume the budget * (those run by tile entities, etc...) will also consume the budget
* <p> * <p>
* Next tick, we add {@link CoreConfig#maxMainGlobalTime} to our budget (clamp it to that value too). If we're still * Next tick, we add {@link MainThreadConfig#maxGlobalTime()} to our budget (clamp it to that value too). If we're still
* over budget, then we should not execute <em>any</em> work (either as part of {@link MainThread} or externally). * over budget, then we should not execute <em>any</em> work (either as part of {@link MainThread} or externally).
*/ */
public final class MainThread implements MainThreadScheduler { public final class MainThread implements MainThreadScheduler {
@ -35,6 +34,8 @@ public final class MainThread implements MainThreadScheduler {
return at < bt ? -1 : 1; return at < bt ? -1 : 1;
}); });
final MainThreadConfig config;
/** /**
* The set of executors which went over budget in a previous tick, and are waiting for their time to run down. * The set of executors which went over budget in a previous tick, and are waiting for their time to run down.
* *
@ -66,6 +67,11 @@ public final class MainThread implements MainThreadScheduler {
private long minimumTime = 0; private long minimumTime = 0;
public MainThread() { public MainThread() {
this(MainThreadConfig.DEFAULT);
}
public MainThread(MainThreadConfig config) {
this.config = config;
} }
void queue(MainThreadExecutor executor) { void queue(MainThreadExecutor executor) {
@ -79,7 +85,7 @@ public final class MainThread implements MainThreadScheduler {
var newRuntime = minimumTime; var newRuntime = minimumTime;
// Slow down new computers a little bit. // Slow down new computers a little bit.
if (executor.virtualTime == 0) newRuntime += CoreConfig.maxMainComputerTime; if (executor.virtualTime == 0) newRuntime += config.maxComputerTime();
executor.virtualTime = Math.max(newRuntime, executor.virtualTime); executor.virtualTime = Math.max(newRuntime, executor.virtualTime);
@ -110,7 +116,8 @@ public final class MainThread implements MainThreadScheduler {
// Of course, we'll go over the MAX_TICK_TIME most of the time, but eventually that overrun will accumulate // Of course, we'll go over the MAX_TICK_TIME most of the time, but eventually that overrun will accumulate
// and we'll skip a whole tick - bringing the average back down again. // and we'll skip a whole tick - bringing the average back down again.
currentTick++; currentTick++;
budget = Math.min(budget + CoreConfig.maxMainGlobalTime, CoreConfig.maxMainGlobalTime); var maxGlobal = config.maxGlobalTime();
budget = Math.min(budget + maxGlobal, maxGlobal);
canExecute = budget > 0; canExecute = budget > 0;
// Cool down any warm computers. // Cool down any warm computers.

View File

@ -0,0 +1,50 @@
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.core.computer.mainthread;
import java.util.concurrent.TimeUnit;
/**
* Options to configure the {@link MainThread}.
*/
public interface MainThreadConfig {
/**
* The default value for {@link #maxGlobalTime()}.
*/
long DEFAULT_MAX_GLOBAL_TIME = TimeUnit.MILLISECONDS.toNanos(10);
/**
* The default value for {@link #maxComputerTime()}.
*/
long DEFAULT_MAX_COMPUTER_TIME = TimeUnit.MILLISECONDS.toNanos(5);
/**
* The default config.
*/
MainThreadConfig DEFAULT = new Basic(DEFAULT_MAX_GLOBAL_TIME, DEFAULT_MAX_COMPUTER_TIME);
/**
* The ideal maximum time that can be spent executing tasks in a tick, in nanoseconds.
*
* @return The max time a that can be spent executing tasks in a single tick.
*/
long maxGlobalTime();
/**
* The ideal maximum time a computer can execute for in a tick, in nanoseconds.
*
* @return The max time a computer can execute in a single tick.
*/
long maxComputerTime();
/**
* A basic implementation of {@link MainThreadConfig}, which works on constant values.
*
* @param maxGlobalTime See {@link MainThreadConfig#maxGlobalTime()}.
* @param maxComputerTime See {@link MainThreadConfig#maxComputerTime()}.
*/
record Basic(long maxGlobalTime, long maxComputerTime) implements MainThreadConfig {
}
}

View File

@ -5,7 +5,6 @@
package dan200.computercraft.core.computer.mainthread; package dan200.computercraft.core.computer.mainthread;
import dan200.computercraft.api.peripheral.WorkMonitor; import dan200.computercraft.api.peripheral.WorkMonitor;
import dan200.computercraft.core.CoreConfig;
import dan200.computercraft.core.computer.Computer; import dan200.computercraft.core.computer.Computer;
import dan200.computercraft.core.metrics.Metrics; import dan200.computercraft.core.metrics.Metrics;
import dan200.computercraft.core.metrics.MetricsObserver; import dan200.computercraft.core.metrics.MetricsObserver;
@ -22,7 +21,7 @@ import java.util.concurrent.TimeUnit;
* those run elsewhere (such as during the turtle's tick). In order to handle this, the executor goes through three * those run elsewhere (such as during the turtle's tick). In order to handle this, the executor goes through three
* stages: * stages:
* <p> * <p>
* When {@link State#COOL}, the computer is allocated {@link CoreConfig#maxMainComputerTime}ns to execute any work * When {@link State#COOL}, the computer is allocated {@link MainThreadConfig#maxComputerTime()}ns to execute any work
* this tick. At the beginning of the tick, we execute as many {@link MainThread} tasks as possible, until our * this tick. At the beginning of the tick, we execute as many {@link MainThread} tasks as possible, until our
* time-frame or the global time frame has expired. * time-frame or the global time frame has expired.
* <p> * <p>
@ -33,13 +32,13 @@ import java.util.concurrent.TimeUnit;
* {@link State#HOT}. This means it will no longer be able to execute {@link MainThread} tasks (though will still * {@link State#HOT}. This means it will no longer be able to execute {@link MainThread} tasks (though will still
* execute tile entity tasks, in order to prevent the main thread from exhausting work every tick). * execute tile entity tasks, in order to prevent the main thread from exhausting work every tick).
* <p> * <p>
* At the beginning of the next tick, we increment the budget e by {@link CoreConfig#maxMainComputerTime} and any * At the beginning of the next tick, we increment the budget e by {@link MainThreadConfig#maxComputerTime()} and any
* {@link State#HOT} executors are marked as {@link State#COOLING}. They will remain cooling until their budget is fully * {@link State#HOT} executors are marked as {@link State#COOLING}. They will remain cooling until their budget is fully
* replenished (is equal to {@link CoreConfig#maxMainComputerTime}). Note, this is different to {@link MainThread}, * replenished (is equal to {@link MainThreadConfig#maxComputerTime()}). Note, this is different to {@link MainThread},
* which allows running when it has any budget left. When cooling, <em>no</em> tasks are executed - be they on the tile * which allows running when it has any budget left. When cooling, <em>no</em> tasks are executed - be they on the tile
* entity or main thread. * entity or main thread.
* <p> * <p>
* This mechanism means that, on average, computers will use at most {@link CoreConfig#maxMainComputerTime}ns per * This mechanism means that, on average, computers will use at most {@link MainThreadConfig#maxComputerTime()}ns per
* second, but one task source will not prevent others from executing. * second, but one task source will not prevent others from executing.
* *
* @see MainThread * @see MainThread
@ -189,7 +188,7 @@ final class MainThreadExecutor implements MainThreadScheduler.Executor {
// #tickCooling() isn't called, and so we didn't overrun the previous tick. // #tickCooling() isn't called, and so we didn't overrun the previous tick.
if (currentTick != scheduler.currentTick()) { if (currentTick != scheduler.currentTick()) {
currentTick = scheduler.currentTick(); currentTick = scheduler.currentTick();
budget = CoreConfig.maxMainComputerTime; budget = scheduler.config.maxComputerTime();
} }
budget -= time; budget -= time;
@ -202,15 +201,16 @@ final class MainThreadExecutor implements MainThreadScheduler.Executor {
} }
/** /**
* Move this executor forward one tick, replenishing the budget by {@link CoreConfig#maxMainComputerTime}. * Move this executor forward one tick, replenishing the budget by {@link MainThreadConfig#maxComputerTime()}.
* *
* @return Whether this executor has cooled down, and so is safe to run again. * @return Whether this executor has cooled down, and so is safe to run again.
*/ */
boolean tickCooling() { boolean tickCooling() {
state = State.COOLING; state = State.COOLING;
currentTick = scheduler.currentTick(); currentTick = scheduler.currentTick();
budget = Math.min(budget + CoreConfig.maxMainComputerTime, CoreConfig.maxMainComputerTime); var maxTime = scheduler.config.maxComputerTime();
if (budget < CoreConfig.maxMainComputerTime) return false; budget = Math.min(budget + maxTime, maxTime);
if (budget < maxTime) return false;
state = State.COOL; state = State.COOL;
synchronized (queueLock) { synchronized (queueLock) {

View File

@ -10,8 +10,8 @@ import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaFunction; import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.core.ComputerContext; import dan200.computercraft.core.ComputerContext;
import dan200.computercraft.core.CoreConfig;
import dan200.computercraft.core.computer.mainthread.MainThread; import dan200.computercraft.core.computer.mainthread.MainThread;
import dan200.computercraft.core.computer.mainthread.MainThreadConfig;
import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.test.core.computer.BasicEnvironment; import dan200.computercraft.test.core.computer.BasicEnvironment;
import dan200.computercraft.test.core.filesystem.MemoryMount; import dan200.computercraft.test.core.filesystem.MemoryMount;
@ -46,10 +46,8 @@ public class ComputerBootstrap {
} }
public static void run(WritableMount mount, Consumer<Computer> setup, int maxTicks) { public static void run(WritableMount mount, Consumer<Computer> setup, int maxTicks) {
CoreConfig.maxMainComputerTime = CoreConfig.maxMainGlobalTime = Integer.MAX_VALUE;
var term = new Terminal(51, 19, true); var term = new Terminal(51, 19, true);
var mainThread = new MainThread(); var mainThread = new MainThread(new MainThreadConfig.Basic(Integer.MAX_VALUE, Integer.MAX_VALUE));
var environment = new BasicEnvironment(mount); var environment = new BasicEnvironment(mount);
var context = new ComputerContext(environment, 1, mainThread); var context = new ComputerContext(environment, 1, mainThread);
final var computer = new Computer(context, environment, term, 0); final var computer = new Computer(context, environment, term, 0);

View File

@ -1,6 +1,6 @@
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers // SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
// //
// SPDX-License-Identifier: LicenseRef-CCPL // SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.core.terminal; package dan200.computercraft.core.terminal;

View File

@ -1,6 +1,6 @@
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers // SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
// //
// SPDX-License-Identifier: LicenseRef-CCPL // SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.core.terminal; package dan200.computercraft.core.terminal;

View File

@ -1,6 +1,6 @@
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers // SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
// //
// SPDX-License-Identifier: LicenseRef-CCPL // SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.test.core; package dan200.computercraft.test.core;

View File

@ -1,6 +1,6 @@
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers // SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
// //
// SPDX-License-Identifier: LicenseRef-CCPL // SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.test.core.terminal; package dan200.computercraft.test.core.terminal;