Refactor process lifecycle to use Start, Tick, Stop methods and metrics

This commit is contained in:
Stone_Red
2026-06-10 02:28:57 +02:00
parent 31e54c1637
commit b3c98b1b1d
7 changed files with 163 additions and 81 deletions
+6 -8
View File
@@ -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
View File
@@ -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");
}
}
} }
+8 -6
View File
@@ -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 -10
View File
@@ -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)
+5 -1
View File
@@ -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)
{ {
+69 -43
View File
@@ -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++;
+14
View File
@@ -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; }
}
}