mirror of
https://github.com/Stone-Red-Code/RemSox.git
synced 2026-09-09 07:46:32 +02:00
Refactor process lifecycle to use Start, Tick, Stop methods and metrics
This commit is contained in:
@@ -34,8 +34,6 @@ public class Kernel : Sys.Kernel
|
|||||||
|
|
||||||
Sys.Mouse.MouseManager.Initialize();
|
Sys.Mouse.MouseManager.Initialize();
|
||||||
Sys.Keyboard.KeyboardManager.Initialize();
|
Sys.Keyboard.KeyboardManager.Initialize();
|
||||||
|
|
||||||
_ = ProcessManager.SpawnProcess<CliProcess>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Run()
|
protected override void Run()
|
||||||
@@ -57,13 +55,13 @@ public class Kernel : Sys.Kernel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread.Sleep(1000);
|
ProcessManager.TickAllProcesses();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TestProcess() : Process("Test Process")
|
public class TestProcess() : Process("Test Process")
|
||||||
{
|
{
|
||||||
internal override void Run(string[] args)
|
internal override void Start(string[] args)
|
||||||
{
|
{
|
||||||
Window window = WindowManager.CreateWindow(this, "Test Window", Point.Empty, new Size(200, 150));
|
Window window = WindowManager.CreateWindow(this, "Test Window", Point.Empty, new Size(200, 150));
|
||||||
window.AutoFlush = true;
|
window.AutoFlush = true;
|
||||||
@@ -78,11 +76,11 @@ public class TestProcess() : Process("Test Process")
|
|||||||
circle.Radius = 40;
|
circle.Radius = 40;
|
||||||
|
|
||||||
SendMessageToAllProcesses(new TestMessage { SenderProcessId = Id });
|
SendMessageToAllProcesses(new TestMessage { SenderProcessId = Id });
|
||||||
|
}
|
||||||
|
|
||||||
while (!StopRequested)
|
internal override void Tick()
|
||||||
{
|
{
|
||||||
Thread.Sleep(1000);
|
// Example tick logic - could be used for animations, timed events, etc.
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void HandleInterProcessMessage(Message message)
|
internal override void HandleInterProcessMessage(Message message)
|
||||||
|
|||||||
+49
-12
@@ -5,29 +5,66 @@ namespace RemSox.Processes;
|
|||||||
|
|
||||||
internal class CliProcess() : Process("Cli")
|
internal class CliProcess() : Process("Cli")
|
||||||
{
|
{
|
||||||
internal override void Run(string[] args)
|
private string currentInput = string.Empty;
|
||||||
|
|
||||||
|
internal override void Start(string[] args)
|
||||||
{
|
{
|
||||||
Console.Clear();
|
Console.Clear();
|
||||||
Console.WriteLine("Welcome RemSox!");
|
Console.WriteLine("Welcome RemSox!");
|
||||||
Console.WriteLine("Type 'help' to see available commands.");
|
Console.WriteLine("Type 'help' to see available commands.");
|
||||||
|
Console.Write("> ");
|
||||||
|
}
|
||||||
|
|
||||||
while (!StopRequested)
|
internal override void Tick()
|
||||||
|
{
|
||||||
|
while (Console.KeyAvailable)
|
||||||
{
|
{
|
||||||
Console.Write("> ");
|
ConsoleKeyInfo key = Console.ReadKey(intercept: true);
|
||||||
|
|
||||||
string? input = Console.ReadLine();
|
switch (key.Key)
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(input))
|
|
||||||
{
|
{
|
||||||
continue;
|
case ConsoleKey.Enter:
|
||||||
}
|
Console.WriteLine();
|
||||||
|
|
||||||
bool handled = CommandManager.TryExecute(input, line => Console.WriteLine(line));
|
HandleCommand(currentInput);
|
||||||
|
|
||||||
if (!handled)
|
currentInput = string.Empty;
|
||||||
{
|
Console.Write("> ");
|
||||||
Console.WriteLine($"\"{input}\" is not a command");
|
break;
|
||||||
|
|
||||||
|
case ConsoleKey.Backspace:
|
||||||
|
if (currentInput.Length > 0)
|
||||||
|
{
|
||||||
|
currentInput = currentInput[..^1];
|
||||||
|
Console.Write("\b \b");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
currentInput += key.KeyChar;
|
||||||
|
Console.Write(key.KeyChar);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal override void Stop()
|
||||||
|
{
|
||||||
|
Console.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void HandleCommand(string input)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(input))
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool handled = CommandManager.TryExecute(
|
||||||
|
input,
|
||||||
|
line => Console.WriteLine(line));
|
||||||
|
|
||||||
|
if (!handled)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"\"{input}\" is not a command");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -10,7 +10,7 @@ internal class DesktopProcess() : Process("Desktop Manager")
|
|||||||
{
|
{
|
||||||
private static bool isGraphicsInitialized = false;
|
private static bool isGraphicsInitialized = false;
|
||||||
|
|
||||||
internal override void Run(string[] args)
|
internal override void Start(string[] args)
|
||||||
{
|
{
|
||||||
if (!isGraphicsInitialized)
|
if (!isGraphicsInitialized)
|
||||||
{
|
{
|
||||||
@@ -54,13 +54,15 @@ internal class DesktopProcess() : Process("Desktop Manager")
|
|||||||
|
|
||||||
testWindow.AutoFlush = true;
|
testWindow.AutoFlush = true;
|
||||||
testWindow.Flush();
|
testWindow.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
while (!StopRequested)
|
internal override void Tick()
|
||||||
|
{
|
||||||
|
WindowManager.Update();
|
||||||
|
|
||||||
|
if (!ProcessManager.IsProcessRunning<TerminalProcess>())
|
||||||
{
|
{
|
||||||
WindowManager.Update();
|
ProcessManager.SpawnProcess<TerminalProcess>();
|
||||||
|
|
||||||
// Sleep slightly to yield CPU to the main CLI thread (approx 60 FPS)
|
|
||||||
//Thread.Sleep(16);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace RemSox.Processes;
|
|||||||
|
|
||||||
public class TerminalProcess : Process
|
public class TerminalProcess : Process
|
||||||
{
|
{
|
||||||
private Window? window;
|
private Window window = null!;
|
||||||
private readonly List<string> history = [];
|
private readonly List<string> history = [];
|
||||||
private string currentInput = "";
|
private string currentInput = "";
|
||||||
private readonly List<Text> textLines = [];
|
private readonly List<Text> textLines = [];
|
||||||
@@ -23,7 +23,7 @@ public class TerminalProcess : Process
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Run(string[] args)
|
internal override void Start(string[] args)
|
||||||
{
|
{
|
||||||
window = WindowManager.CreateWindow(this, "Terminal", new Point(50, 50), new Size(400, 300));
|
window = WindowManager.CreateWindow(this, "Terminal", new Point(50, 50), new Size(400, 300));
|
||||||
|
|
||||||
@@ -32,19 +32,20 @@ public class TerminalProcess : Process
|
|||||||
|
|
||||||
window.Flush();
|
window.Flush();
|
||||||
window.OnKeyEvent += HandleKey;
|
window.OnKeyEvent += HandleKey;
|
||||||
|
}
|
||||||
|
|
||||||
while (!StopRequested)
|
internal override void Tick()
|
||||||
|
{
|
||||||
|
if (window.Size != lastSize)
|
||||||
{
|
{
|
||||||
if (window.Size != lastSize)
|
lastSize = window.Size;
|
||||||
{
|
UpdateDisplay();
|
||||||
lastSize = window.Size;
|
|
||||||
UpdateDisplay();
|
|
||||||
}
|
|
||||||
Thread.Sleep(50);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void Stop()
|
||||||
|
{
|
||||||
window.OnKeyEvent -= HandleKey;
|
window.OnKeyEvent -= HandleKey;
|
||||||
WindowManager.CloseWindow(window);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleKey(KeyEvent keyEvent)
|
private void HandleKey(KeyEvent keyEvent)
|
||||||
|
|||||||
@@ -15,7 +15,11 @@ public abstract class Process(string name)
|
|||||||
|
|
||||||
public bool IsRunning => !StopRequested;
|
public bool IsRunning => !StopRequested;
|
||||||
|
|
||||||
internal abstract void Run(string[] args);
|
internal virtual void Start(string[] args) { }
|
||||||
|
|
||||||
|
internal abstract void Tick();
|
||||||
|
|
||||||
|
internal virtual void Stop() { }
|
||||||
|
|
||||||
internal virtual void HandleInterProcessMessage(Message message)
|
internal virtual void HandleInterProcessMessage(Message message)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,12 +2,13 @@ using RemSox.Logging;
|
|||||||
using RemSox.UI.GUI.Windows;
|
using RemSox.UI.GUI.Windows;
|
||||||
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace RemSox.Processing;
|
namespace RemSox.Processing;
|
||||||
|
|
||||||
public static class ProcessManager
|
public static class ProcessManager
|
||||||
{
|
{
|
||||||
private static readonly ConcurrentDictionary<int, (Process Process, Thread Thread)> processes = new();
|
private static readonly ConcurrentDictionary<int, (Process Process, ProcessMetrics Metrics)> processes = new();
|
||||||
|
|
||||||
private static readonly ConcurrentDictionary<Type, ConcurrentHashSet<int>> processesByType = new();
|
private static readonly ConcurrentDictionary<Type, ConcurrentHashSet<int>> processesByType = new();
|
||||||
|
|
||||||
@@ -47,40 +48,16 @@ public static class ProcessManager
|
|||||||
return set;
|
return set;
|
||||||
});
|
});
|
||||||
|
|
||||||
Thread thread = new(() =>
|
try
|
||||||
{
|
{
|
||||||
try
|
process.Start(args ?? []);
|
||||||
{
|
}
|
||||||
process.Run(args ?? []);
|
catch (Exception ex)
|
||||||
}
|
{
|
||||||
catch (Exception ex)
|
logger.Log($"Process {process.Name} (ID: {process.Id}) terminated with an exception: {ex}", LogSeverity.Error);
|
||||||
{
|
}
|
||||||
logger.Log($"Process {process.Name} (ID: {process.Id}) terminated with an exception: {ex}", LogSeverity.Error);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_ = processes.TryRemove(id, out _);
|
|
||||||
|
|
||||||
if (processesByType.TryGetValue(typeof(T), out ConcurrentHashSet<int>? set))
|
|
||||||
{
|
|
||||||
_ = set.TryRemove(id);
|
|
||||||
if (set.Count == 0)
|
|
||||||
{
|
|
||||||
_ = processesByType.TryRemove(typeof(T), out _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WindowManager.CloseWindowsForProcess(id);
|
|
||||||
|
|
||||||
logger.Log($"Process {process.Name} (ID: {process.Id}) has stopped.", LogSeverity.Info);
|
|
||||||
|
|
||||||
_ = processLoggers.TryRemove(id, out _);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
_ = processes.TryAdd(id, (process, thread));
|
|
||||||
thread.Start();
|
|
||||||
|
|
||||||
|
_ = processes.TryAdd(id, (process, new ProcessMetrics()));
|
||||||
logger.Log($"Spawned process {process.Name} of type {typeof(T).Name} with ID {id}.", LogSeverity.Info);
|
logger.Log($"Spawned process {process.Name} of type {typeof(T).Name} with ID {id}.", LogSeverity.Info);
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
@@ -88,7 +65,7 @@ public static class ProcessManager
|
|||||||
|
|
||||||
public static void StopProcess(int processId, bool waitForExit = false)
|
public static void StopProcess(int processId, bool waitForExit = false)
|
||||||
{
|
{
|
||||||
if (!processes.TryGetValue(processId, out (Process Process, Thread Thread) entry))
|
if (!processes.TryGetValue(processId, out (Process Process, ProcessMetrics Metrics) entry))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -99,21 +76,18 @@ public static class ProcessManager
|
|||||||
|
|
||||||
public static async Task StopProcessAndWaitAsync(int processId)
|
public static async Task StopProcessAndWaitAsync(int processId)
|
||||||
{
|
{
|
||||||
if (!processes.TryGetValue(processId, out (Process Process, Thread Thread) entry))
|
if (!processes.TryGetValue(processId, out (Process Process, ProcessMetrics Metrics) entry))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Log($"Requesting stop of process {entry.Process.Name} (ID: {entry.Process.Id}).", LogSeverity.Info);
|
logger.Log($"Requesting stop of process {entry.Process.Name} (ID: {entry.Process.Id}).", LogSeverity.Info);
|
||||||
entry.Process.RequestStop();
|
entry.Process.RequestStop();
|
||||||
|
|
||||||
logger.Log($"Waiting for process {entry.Process.Name} (ID: {entry.Process.Id}) to stop.", LogSeverity.Info);
|
|
||||||
await Task.Run(entry.Thread.Join);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void StopAllProcesses()
|
public static void StopAllProcesses()
|
||||||
{
|
{
|
||||||
foreach ((Process Process, Thread Thread) entry in processes.Values)
|
foreach ((Process Process, ProcessMetrics Metrics) entry in processes.Values)
|
||||||
{
|
{
|
||||||
StopProcess(entry.Process.Id);
|
StopProcess(entry.Process.Id);
|
||||||
}
|
}
|
||||||
@@ -123,7 +97,7 @@ public static class ProcessManager
|
|||||||
|
|
||||||
public static Process? GetProcess(int processId)
|
public static Process? GetProcess(int processId)
|
||||||
{
|
{
|
||||||
if (processes.TryGetValue(processId, out (Process Process, Thread Thread) entry))
|
if (processes.TryGetValue(processId, out (Process Process, ProcessMetrics Metrics) entry))
|
||||||
{
|
{
|
||||||
return entry.Process;
|
return entry.Process;
|
||||||
}
|
}
|
||||||
@@ -142,7 +116,7 @@ public static class ProcessManager
|
|||||||
{
|
{
|
||||||
foreach (int processId in set)
|
foreach (int processId in set)
|
||||||
{
|
{
|
||||||
if (processes.TryGetValue(processId, out (Process Process, Thread Thread) entry) && entry.Process is T typedProcess)
|
if (processes.TryGetValue(processId, out (Process Process, ProcessMetrics Metrics) entry) && entry.Process is T typedProcess)
|
||||||
{
|
{
|
||||||
yield return typedProcess;
|
yield return typedProcess;
|
||||||
}
|
}
|
||||||
@@ -152,7 +126,7 @@ public static class ProcessManager
|
|||||||
|
|
||||||
public static IEnumerable<Process> GetAllProcesses()
|
public static IEnumerable<Process> GetAllProcesses()
|
||||||
{
|
{
|
||||||
foreach ((Process Process, Thread Thread) entry in processes.Values)
|
foreach ((Process Process, ProcessMetrics Metrics) entry in processes.Values)
|
||||||
{
|
{
|
||||||
yield return entry.Process;
|
yield return entry.Process;
|
||||||
}
|
}
|
||||||
@@ -160,7 +134,7 @@ public static class ProcessManager
|
|||||||
|
|
||||||
public static bool TryGetProcess(int processId, out Process? process)
|
public static bool TryGetProcess(int processId, out Process? process)
|
||||||
{
|
{
|
||||||
if (processes.TryGetValue(processId, out (Process Process, Thread Thread) entry))
|
if (processes.TryGetValue(processId, out (Process Process, ProcessMetrics Metrics) entry))
|
||||||
{
|
{
|
||||||
process = entry.Process;
|
process = entry.Process;
|
||||||
return true;
|
return true;
|
||||||
@@ -185,6 +159,58 @@ public static class ProcessManager
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static void TickAllProcesses()
|
||||||
|
{
|
||||||
|
foreach ((Process Process, ProcessMetrics Metrics) entry in processes.Values)
|
||||||
|
{
|
||||||
|
if (entry.Process.IsRunning)
|
||||||
|
{
|
||||||
|
Stopwatch stopwatch = Stopwatch.StartNew();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
entry.Process.Tick();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.Log($"Process {entry.Process.Name} (ID: {entry.Process.Id}) threw an exception during Tick: {ex}", LogSeverity.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
stopwatch.Stop();
|
||||||
|
|
||||||
|
int tickTimeMs = (int)stopwatch.ElapsedMilliseconds;
|
||||||
|
entry.Metrics.LastTickTimeMs = tickTimeMs;
|
||||||
|
entry.Metrics.AverageTickTimeMs = (entry.Metrics.AverageTickTimeMs * 7 + tickTimeMs) / 8;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CleanupProcess(entry.Process);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CleanupProcess(Process process)
|
||||||
|
{
|
||||||
|
process.Stop();
|
||||||
|
|
||||||
|
_ = processes.TryRemove(process.Id, out _);
|
||||||
|
|
||||||
|
if (processesByType.TryGetValue(process.GetType(), out ConcurrentHashSet<int>? set))
|
||||||
|
{
|
||||||
|
_ = set.TryRemove(process.Id);
|
||||||
|
if (set.Count == 0)
|
||||||
|
{
|
||||||
|
_ = processesByType.TryRemove(process.GetType(), out _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowManager.CloseWindowsForProcess(process.Id);
|
||||||
|
|
||||||
|
logger.Log($"Process {process.Name} (ID: {process.Id}) has stopped.", LogSeverity.Info);
|
||||||
|
|
||||||
|
_ = processLoggers.TryRemove(process.Id, out _);
|
||||||
|
}
|
||||||
|
|
||||||
private static int GetNextProcessId()
|
private static int GetNextProcessId()
|
||||||
{
|
{
|
||||||
return nextProcessId++;
|
return nextProcessId++;
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace RemSox.Processing
|
||||||
|
{
|
||||||
|
public class ProcessMetrics
|
||||||
|
{
|
||||||
|
public int AverageTickTimeMs { get; set; }
|
||||||
|
|
||||||
|
public int LastTickTimeMs { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user