1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-01-27 17:34:48 +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.GlobalEnvironment;
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.ILuaMachine;
import dan200.computercraft.impl.AbstractComputerCraftAPI;
@ -65,7 +66,7 @@ public final class ServerContext {
private ServerContext(MinecraftServer server) {
this.server = server;
storageDir = server.getWorldPath(FOLDER);
mainThread = new MainThread();
mainThread = new MainThread(mainThreadConfig);
context = new ComputerContext(
new Environment(server),
new ComputerThread(ConfigSpec.computerThreads.get()),
@ -215,4 +216,16 @@ public final class ServerContext {
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.apis.http.NetworkUtils;
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.platform.PlatformHelper;
import org.apache.logging.log4j.LogManager;
@ -155,14 +156,14 @@ public final class ConfigSpec {
milliseconds.
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.""")
.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
.comment("""
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
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();
}
@ -348,10 +349,6 @@ public final class ConfigSpec {
CoreConfig.defaultComputerSettings = defaultComputerSettings.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.
var logFilter = MarkerFilter.createFilter(
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.OptionalInt;
import java.util.concurrent.TimeUnit;
/**
* Config options for ComputerCraft's Lua runtime.
@ -25,9 +24,6 @@ public final class CoreConfig {
public static boolean disableLua51Features = false;
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 httpWebsocketEnabled = true;
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.
*
* @see dan200.computercraft.api.ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
* @see ILuaAPIFactory
* @see ApiWrapper
*/

View File

@ -4,7 +4,6 @@
package dan200.computercraft.core.computer.mainthread;
import dan200.computercraft.core.CoreConfig;
import dan200.computercraft.core.metrics.MetricsObserver;
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
* (those run by tile entities, etc...) will also consume the budget
* <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).
*/
public final class MainThread implements MainThreadScheduler {
@ -35,6 +34,8 @@ public final class MainThread implements MainThreadScheduler {
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.
*
@ -66,6 +67,11 @@ public final class MainThread implements MainThreadScheduler {
private long minimumTime = 0;
public MainThread() {
this(MainThreadConfig.DEFAULT);
}
public MainThread(MainThreadConfig config) {
this.config = config;
}
void queue(MainThreadExecutor executor) {
@ -79,7 +85,7 @@ public final class MainThread implements MainThreadScheduler {
var newRuntime = minimumTime;
// 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);
@ -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
// and we'll skip a whole tick - bringing the average back down again.
currentTick++;
budget = Math.min(budget + CoreConfig.maxMainGlobalTime, CoreConfig.maxMainGlobalTime);
var maxGlobal = config.maxGlobalTime();
budget = Math.min(budget + maxGlobal, maxGlobal);
canExecute = budget > 0;
// 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;
import dan200.computercraft.api.peripheral.WorkMonitor;
import dan200.computercraft.core.CoreConfig;
import dan200.computercraft.core.computer.Computer;
import dan200.computercraft.core.metrics.Metrics;
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
* stages:
* <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
* time-frame or the global time frame has expired.
* <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
* execute tile entity tasks, in order to prevent the main thread from exhausting work every tick).
* <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
* 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
* entity or main thread.
* <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.
*
* @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.
if (currentTick != scheduler.currentTick()) {
currentTick = scheduler.currentTick();
budget = CoreConfig.maxMainComputerTime;
budget = scheduler.config.maxComputerTime();
}
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.
*/
boolean tickCooling() {
state = State.COOLING;
currentTick = scheduler.currentTick();
budget = Math.min(budget + CoreConfig.maxMainComputerTime, CoreConfig.maxMainComputerTime);
if (budget < CoreConfig.maxMainComputerTime) return false;
var maxTime = scheduler.config.maxComputerTime();
budget = Math.min(budget + maxTime, maxTime);
if (budget < maxTime) return false;
state = State.COOL;
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.LuaFunction;
import dan200.computercraft.core.ComputerContext;
import dan200.computercraft.core.CoreConfig;
import dan200.computercraft.core.computer.mainthread.MainThread;
import dan200.computercraft.core.computer.mainthread.MainThreadConfig;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.test.core.computer.BasicEnvironment;
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) {
CoreConfig.maxMainComputerTime = CoreConfig.maxMainGlobalTime = Integer.MAX_VALUE;
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 context = new ComputerContext(environment, 1, mainThread);
final var computer = new Computer(context, environment, term, 0);

View File

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

View File

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

View File

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

View File

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