NETSCRIPT: Greatly speed up script launching, and remove the limitation unique args per script (#440)

* Remove the limitation unique args per script
* Internal changes to how runningScripts are stored on the server, to make common usage faster.
This commit is contained in:
David Walker
2023-04-27 15:21:06 -07:00
committed by GitHub
parent f81297dcd6
commit aa7facd4ba
44 changed files with 573 additions and 493 deletions
+7 -4
View File
@@ -1,6 +1,6 @@
import { Terminal } from "../../Terminal";
import { BaseServer } from "../../Server/BaseServer";
import { findRunningScript } from "../../Script/ScriptHelpers";
import { findRunningScripts } from "../../Script/ScriptHelpers";
import { hasScriptExtension, validScriptExtensions } from "../../Paths/ScriptFilePath";
export function check(args: (string | number | boolean)[], server: BaseServer): void {
@@ -16,8 +16,11 @@ export function check(args: (string | number | boolean)[], server: BaseServer):
}
// Check that the script is running on this machine
const runningScript = findRunningScript(scriptName, args.slice(1), server);
if (runningScript == null) return Terminal.error(`No script named ${scriptName} is running on the server`);
runningScript.displayLog();
const runningScripts = findRunningScripts(scriptName, args.slice(1), server);
if (runningScripts === null) {
Terminal.error(`No script named ${scriptName} is running on the server`);
return;
}
runningScripts.values().next().value.displayLog();
}
}
+43 -18
View File
@@ -1,25 +1,50 @@
import { Terminal } from "../../Terminal";
import { BaseServer } from "../../Server/BaseServer";
import { killWorkerScript } from "../../Netscript/killWorkerScript";
import { findRunningScripts } from "../../Script/ScriptHelpers";
import { killWorkerScriptByPid } from "../../Netscript/killWorkerScript";
import { hasScriptExtension } from "../../Paths/ScriptFilePath";
import type { BaseServer } from "../../Server/BaseServer";
export function kill(args: (string | number | boolean)[], server: BaseServer): void {
if (args.length < 1) {
return Terminal.error("Incorrect usage of kill command. Usage: kill [pid] or kill [scriptname] [arg1] [arg2]...");
}
if (typeof args[0] === "number") {
const pid = args[0];
if (killWorkerScript(pid)) return Terminal.print(`Killing script with PID ${pid}`);
}
// Shift args doesn't need to be sliced to check runningScript args
const fileName = String(args.shift());
const path = Terminal.getFilepath(fileName);
if (!path) return Terminal.error(`Could not parse filename: ${fileName}`);
if (!hasScriptExtension(path)) return Terminal.error(`${path} does not have a script file extension`);
try {
if (args.length < 1 || typeof args[0] === "boolean") {
Terminal.error("Incorrect usage of kill command. Usage: kill [pid] or kill [scriptname] [arg1] [arg2]...");
return;
}
const runningScript = server.getRunningScript(path, args);
if (runningScript == null) return Terminal.error("No such script is running. Nothing to kill");
// Kill by PID
if (typeof args[0] === "number") {
const pid = args[0];
const res = killWorkerScriptByPid(pid);
if (res) {
Terminal.print(`Killing script with PID ${pid}`);
} else {
Terminal.error(`Failed to kill script with PID ${pid}. No such script is running`);
}
killWorkerScript(runningScript.pid);
Terminal.print(`Killing ${path}`);
return;
}
const path = Terminal.getFilepath(args[0]);
if (!path) return Terminal.error(`Invalid filename: ${args[0]}`);
if (!hasScriptExtension(path)) return Terminal.error(`Invalid file extension. Kill can only be used on scripts.`);
const runningScripts = findRunningScripts(path, args.slice(1), server);
if (runningScripts === null) {
Terminal.error("No such script is running. Nothing to kill");
return;
}
let killed = 0;
for (const pid of runningScripts.keys()) {
killed++;
if (killed < 5) {
Terminal.print(`Killing ${path} with pid ${pid}`);
}
killWorkerScriptByPid(pid);
}
if (killed >= 5) {
Terminal.print(`... killed ${killed} instances total`);
}
} catch (e) {
Terminal.error(e + "");
}
}
+6 -4
View File
@@ -1,10 +1,12 @@
import { Terminal } from "../../Terminal";
import { BaseServer } from "../../Server/BaseServer";
import { killWorkerScript } from "../../Netscript/killWorkerScript";
import { WorkerScriptStartStopEventEmitter } from "../../Netscript/WorkerScriptStartStopEventEmitter";
import { killWorkerScriptByPid } from "../../Netscript/killWorkerScript";
export function killall(_args: (string | number | boolean)[], server: BaseServer): void {
Terminal.print("Killing all running scripts");
for (const runningScript of server.runningScripts) killWorkerScript(runningScript.pid);
WorkerScriptStartStopEventEmitter.emit();
for (const byPid of server.runningScriptMap.values()) {
for (const runningScript of byPid.values()) {
killWorkerScriptByPid(runningScript.pid);
}
}
}
+9 -19
View File
@@ -1,5 +1,6 @@
import { Terminal } from "../../Terminal";
import { BaseServer } from "../../Server/BaseServer";
import { matchScriptPathUnanchored } from "../../utils/helpers/scriptKey";
import * as libarg from "arg";
export function ps(args: (string | number | boolean)[], server: BaseServer): void {
@@ -17,26 +18,15 @@ export function ps(args: (string | number | boolean)[], server: BaseServer): voi
Terminal.error("Incorrect usage of ps command. Usage: ps [-g, --grep pattern]");
return;
}
const pattern = flags["--grep"];
if (pattern) {
const re = new RegExp(pattern.toString());
const matching = server.runningScripts.filter((x) => re.test(x.filename));
for (let i = 0; i < matching.length; i++) {
const rsObj = matching[i];
let res = `(PID - ${rsObj.pid}) ${rsObj.filename}`;
for (let j = 0; j < rsObj.args.length; ++j) {
res += " " + rsObj.args[j].toString();
}
Terminal.print(res);
}
let pattern = flags["--grep"];
if (!pattern) {
pattern = ".*"; // Match anything
}
if (args.length === 0) {
for (let i = 0; i < server.runningScripts.length; i++) {
const rsObj = server.runningScripts[i];
let res = `(PID - ${rsObj.pid}) ${rsObj.filename}`;
for (let j = 0; j < rsObj.args.length; ++j) {
res += " " + rsObj.args[j].toString();
}
const re = matchScriptPathUnanchored(pattern);
for (const [k, byPid] of server.runningScriptMap) {
if (!re.test(k)) continue;
for (const rsObj of byPid.values()) {
const res = `(PID - ${rsObj.pid}) ${rsObj.filename} ${rsObj.args.join(" ")}`;
Terminal.print(res);
}
}
-5
View File
@@ -28,11 +28,6 @@ export function runScript(path: ScriptFilePath, commandArgs: (string | number |
// Todo: Switch out arg for something with typescript support
const args = flags["_"] as ScriptArg[];
// Check if this script is already running
if (server.getRunningScript(path, args)) {
return Terminal.error("This script is already running with the same args.");
}
const singleRamUsage = script.getRamUsage(server.scripts);
if (!singleRamUsage) return Terminal.error("Error while calculating ram usage for this script.");
+27 -55
View File
@@ -1,66 +1,38 @@
import { Terminal } from "../../Terminal";
import { BaseServer } from "../../Server/BaseServer";
import { findRunningScriptByPid } from "../../Script/ScriptHelpers";
import { compareArrays } from "../../utils/helpers/compareArrays";
import { findRunningScripts, findRunningScriptByPid } from "../../Script/ScriptHelpers";
import { LogBoxEvents } from "../../ui/React/LogBoxManager";
import { hasScriptExtension } from "../../Paths/ScriptFilePath";
export function tail(commandArray: (string | number | boolean)[], server: BaseServer): void {
if (commandArray.length < 1) {
return Terminal.error("Incorrect number of arguments. Usage: tail [script] [arg1] [arg2]...");
}
if (typeof commandArray[0] === "number") {
const runningScript = findRunningScriptByPid(commandArray[0], server);
if (!runningScript) return Terminal.error(`No script with PID ${commandArray[0]} is running on the server`);
LogBoxEvents.emit(runningScript);
return;
}
try {
if (commandArray.length < 1) {
Terminal.error("Incorrect number of arguments. Usage: tail [script] [arg1] [arg2]...");
} else if (typeof commandArray[0] === "string") {
const [rawName, ...args] = commandArray;
const path = Terminal.getFilepath(rawName);
if (!path) return Terminal.error(`Invalid filename: ${rawName}`);
if (!hasScriptExtension(path)) return Terminal.error(`Invalid file extension. Tail can only be used on scripts.`);
const path = Terminal.getFilepath(String(commandArray[0]));
if (!path) return Terminal.error(`Invalid file path: ${commandArray[0]}`);
if (!hasScriptExtension(path)) return Terminal.error(`Invalid file extension. Tail can only be used on scripts.`);
const candidates = findRunningScripts(path, args, server);
// Get script arguments
const args = [];
for (let i = 1; i < commandArray.length; ++i) {
args.push(commandArray[i]);
}
// go over all the running scripts. If there's a perfect
// match, use it!
for (let i = 0; i < server.runningScripts.length; ++i) {
if (server.runningScripts[i].filename === path && compareArrays(server.runningScripts[i].args, args)) {
LogBoxEvents.emit(server.runningScripts[i]);
return;
// if there's no candidate then we just don't know.
if (candidates === null) {
Terminal.error(`No script named ${path} with args ${JSON.stringify(args)} is running on the server`);
return;
}
// Just use the first one (if there are multiple with the same
// arguments, they can't be distinguished except by pid).
LogBoxEvents.emit(candidates.values().next().value);
} else if (typeof commandArray[0] === "number") {
const runningScript = findRunningScriptByPid(commandArray[0], server);
if (runningScript == null) {
Terminal.error(`No script with PID ${commandArray[0]} is running on the server`);
return;
}
LogBoxEvents.emit(runningScript);
}
} catch (e) {
Terminal.error(e + "");
}
// Find all scripts that are potential candidates.
const candidates = [];
for (let i = 0; i < server.runningScripts.length; ++i) {
// only scripts that have more arguments (equal arguments is already caught)
if (server.runningScripts[i].args.length < args.length) continue;
// make a smaller copy of the args.
const args2 = server.runningScripts[i].args.slice(0, args.length);
if (server.runningScripts[i].filename === path && compareArrays(args2, args)) {
candidates.push(server.runningScripts[i]);
}
}
// If there's only 1 possible choice, use that.
if (candidates.length === 1) {
LogBoxEvents.emit(candidates[0]);
return;
}
// otherwise lists all possible conflicting choices.
if (candidates.length > 1) {
Terminal.error("Found several potential candidates:");
for (const candidate of candidates) Terminal.error(`${candidate.filename} ${candidate.args.join(" ")}`);
Terminal.error("Script arguments need to be specified.");
return;
}
// if there's no candidate then we just don't know.
Terminal.error(`No script named ${path} is running on the server`);
}
+19 -19
View File
@@ -26,29 +26,29 @@ export function top(args: (string | number | boolean)[], server: BaseServer): vo
Terminal.print(headers);
const currRunningScripts = server.runningScripts;
const currRunningScripts = server.runningScriptMap;
// Iterate through scripts on current server
for (let i = 0; i < currRunningScripts.length; i++) {
const script = currRunningScripts[i];
for (const byPid of currRunningScripts.values()) {
for (const script of byPid.values()) {
// Calculate name padding
const numSpacesScript = Math.max(0, scriptWidth - script.filename.length);
const spacesScript = " ".repeat(numSpacesScript);
// Calculate name padding
const numSpacesScript = Math.max(0, scriptWidth - script.filename.length);
const spacesScript = " ".repeat(numSpacesScript);
// Calculate PID padding
const numSpacesPid = Math.max(0, pidWidth - (script.pid + "").length);
const spacesPid = " ".repeat(numSpacesPid);
// Calculate PID padding
const numSpacesPid = Math.max(0, pidWidth - (script.pid + "").length);
const spacesPid = " ".repeat(numSpacesPid);
// Calculate thread padding
const numSpacesThread = Math.max(0, threadsWidth - (script.threads + "").length);
const spacesThread = " ".repeat(numSpacesThread);
// Calculate thread padding
const numSpacesThread = Math.max(0, threadsWidth - (script.threads + "").length);
const spacesThread = " ".repeat(numSpacesThread);
// Calculate and transform RAM usage
const ramUsage = formatRam(script.ramUsage * script.threads);
// Calculate and transform RAM usage
const ramUsage = formatRam(script.ramUsage * script.threads);
const entry = [script.filename, spacesScript, script.pid, spacesPid, script.threads, spacesThread, ramUsage].join(
"",
);
Terminal.print(entry);
const entry = [script.filename, spacesScript, script.pid, spacesPid, script.threads, spacesThread, ramUsage].join(
"",
);
Terminal.print(entry);
}
}
}