SETTINGS: Add infinite loop detection setting (#402)

This commit is contained in:
Snarling 2023-02-28 06:54:01 -05:00 committed by GitHub
parent ae92ea0f94
commit ee3c412933
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 34 additions and 0 deletions

@ -140,6 +140,17 @@ export const SystemPage = (): React.ReactElement => {
marks marks
/> />
</> </>
<OptionSwitch
checked={Settings.infiniteLoopDetection}
onChange={(newValue) => (Settings.infiniteLoopDetection = newValue)}
text="Infinite loop detection"
tooltip={
<>
If this is set, scripts can generate an execution timeout error when an ns function is called and the script
has been running synchronous operations for at least 7s.
</>
}
/>
<OptionSwitch <OptionSwitch
checked={Settings.SuppressSavedGameToast} checked={Settings.SuppressSavedGameToast}
onChange={(newValue) => (Settings.SuppressSavedGameToast = newValue)} onChange={(newValue) => (Settings.SuppressSavedGameToast = newValue)}

@ -33,6 +33,7 @@ import { BaseServer } from "../Server/BaseServer";
import { dialogBoxCreate } from "../ui/React/DialogBox"; import { dialogBoxCreate } from "../ui/React/DialogBox";
import { checkEnum } from "../utils/helpers/enum"; import { checkEnum } from "../utils/helpers/enum";
import { RamCostConstants } from "./RamCostGenerator"; import { RamCostConstants } from "./RamCostGenerator";
import { Settings } from "../Settings/Settings";
export const helpers = { export const helpers = {
string, string,
@ -303,6 +304,15 @@ function checkSingularityAccess(ctx: NetscriptContext): void {
} }
} }
/** The last time the page was able to update this tracking variable, which is used for timeout detection */
let lastTime = Date.now();
setInterval(() => (lastTime = Date.now()), 1000);
// This event should prevent false positive timeout errors when user alt-tabs / otherwise hides the window
document.addEventListener(
"visibilitychange",
() => (lastTime = document.visibilityState === "hidden" ? Infinity : Date.now()),
);
/** Create an error if a script is dead or if concurrent ns function calls are made */ /** Create an error if a script is dead or if concurrent ns function calls are made */
function checkEnvFlags(ctx: NetscriptContext): void { function checkEnvFlags(ctx: NetscriptContext): void {
const ws = ctx.workerScript; const ws = ctx.workerScript;
@ -323,6 +333,17 @@ function checkEnvFlags(ctx: NetscriptContext): void {
"CONCURRENCY", "CONCURRENCY",
); );
} }
// 8s allows scripts at least 7s of synchronous execution time before error, since updates are every 1s.
if (Settings.infiniteLoopDetection && Date.now() - 8000 > lastTime) {
// Prevent error getting piggybacked to another script due to a stale timer.
lastTime = Date.now();
throw makeRuntimeErrorMsg(
ctx,
"Possible infinite loop detected while running function.\nThis error is enabled by Options -> System -> Infinite loop detection.",
"EXECUTION TIMEOUT",
);
}
} }
/** Set a timeout for performing a task, mark the script as busy in the meantime. */ /** Set a timeout for performing a task, mark the script as busy in the meantime. */

@ -102,6 +102,8 @@ export const Settings = {
useEngineeringNotation: false, useEngineeringNotation: false,
/** Whether to disable suffixes and always use exponential form (scientific or engineering). */ /** Whether to disable suffixes and always use exponential form (scientific or engineering). */
disableSuffixes: false, disableSuffixes: false,
/** Whether to check for execution timeout errors */
infiniteLoopDetection: true,
load(saveString: string) { load(saveString: string) {
const save = JSON.parse(saveString); const save = JSON.parse(saveString);