Capture and surface game server exit codes in LocalMultiplayerAgent#209
Draft
Capture and surface game server exit codes in LocalMultiplayerAgent#209
Conversation
- IProcessWrapper.WaitForProcessExit now returns int (exit code) - ProcessWrapper tracks started processes in ConcurrentDictionary for reliable exit code capture even when process crashes early - ISessionHostRunner.WaitOnServerExit now returns Task<int> - ProcessRunner and DockerContainerEngine return exit codes - MultiplayerServerManager logs and prints the exit code to console Agent-Logs-Url: https://github.com/PlayFab/MpsAgent/sessions/99f02fa0-80b5-470b-afbc-8b85cf45d9fa Co-authored-by: dgkanatsios <8256138+dgkanatsios@users.noreply.github.com>
Copilot created this pull request from a session on behalf of
dgkanatsios
April 8, 2026 08:03
View session
There was a problem hiding this comment.
Pull request overview
This PR updates the LocalMultiplayerAgent/VmAgent runner abstractions to capture and propagate game server exit codes (both process and container based), so crashes/non-zero exits are surfaced to logs/console instead of being silently discarded.
Changes:
- Updated runner/process-wrapper interfaces to return exit codes (
WaitForProcessExit→int,WaitOnServerExit→Task<int>). - Added process tracking in
ProcessWrapperto reliably retrieve exit codes withoutGetProcessByIdraces. - Plumbed exit codes through
ProcessRunnerandDockerContainerEnginetoMultiplayerServerManagerfor logging/console output.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| VmAgent.Core/Interfaces/ProcessWrapper.cs | Track started Process instances and return ExitCode from WaitForProcessExit. |
| VmAgent.Core/Interfaces/IProcessWrapper.cs | Interface updated to return an exit code from WaitForProcessExit. |
| VmAgent.Core/Interfaces/ProcessRunner.cs | Return/log process exit code from WaitOnServerExit. |
| VmAgent.Core/Interfaces/DockerContainerEngine.cs | Return/log container exit code from WaitOnServerExit (previously logged only). |
| VmAgent.Core/Interfaces/ISessionHostRunner.cs | Interface updated: WaitOnServerExit now returns Task<int>. |
| VmAgent.Core/Interfaces/BaseSessionHostRunner.cs | Abstract signature updated to Task<int> WaitOnServerExit(...). |
| LocalMultiplayerAgent/MultiplayerServerManager.cs | Capture returned exit code and write it to logger + console. |
Comments suppressed due to low confidence (1)
VmAgent.Core/Interfaces/ProcessWrapper.cs:22
- Now that ProcessWrapper retains Process objects in _trackedProcesses, Kill(int id) should prefer the tracked Process reference (and remove/dispose it) instead of calling Process.GetProcessById. Using GetProcessById after the original process has exited can hit PID reuse and potentially kill an unrelated process; tracking was introduced specifically to avoid this class of race.
private readonly ConcurrentDictionary<int, Process> _trackedProcesses = new ConcurrentDictionary<int, Process>();
public void Kill(int id)
{
try
{
Process process = Process.GetProcessById(id);
process.Kill(true);
}
7eba5f1 to
02576fe
Compare
- ProcessWrapper.Kill() now uses tracked Process reference via TryRemove (prevents dictionary leak in production VmAgent path where WaitForProcessExit is never called) - Both methods now Dispose Process to release native OS handles - WaitForProcessExit() cancels async output/error reads before Dispose - WaitForProcessExit() throws if process not tracked (fail fast) - Start() adds null-check for Process.Start() return value - ProcessWrapper implements IDisposable for shutdown cleanup - Add ProcessWrapperTests covering cleanup, exit code, and Dispose - Add DockerContainerEngine.WaitOnServerExit test for exit code Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
02576fe to
e610aa6
Compare
- Kill(): use 'using' statement instead of try/finally+Dispose - WaitForProcessExit(): use 'using' statement, add Debug.WriteLine to empty catch blocks for CancelOutputRead/CancelErrorRead - Tests: use specific catch types instead of generic catch clause - Tests: refactor helper methods to expression-bodied ternaries - Tests: assert InvalidOperationException (not mixed exception types) now that WaitForProcessExit throws for untracked processes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1826ab3 to
e5f3660
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Game server exit codes were silently discarded. For containers, the code logged the exit code but never returned it. For processes, exit codes were never captured at all. This made it impossible to diagnose crashes or non-zero exits from the game server.
Interface changes
IProcessWrapper.WaitForProcessExit:void→intISessionHostRunner.WaitOnServerExit:Task→Task<int>BaseSessionHostRunnerabstract signature updated accordinglyProcessWrapper: retain Process references to avoid
GetProcessByIdpitfallsProcess.GetProcessById(pid)throwsArgumentExceptionif the process has already exited, and is subject to PID reuse races. Both make exit code retrieval unreliable for short-lived or crashing servers.ProcessWrappernow tracks startedProcessobjects in aConcurrentDictionaryand retrieves them by ID inWaitForProcessExit, falling back toGetProcessByIdonly for externally-started processes.Runner implementations
ProcessRunner— returns and logs exit code fromProcessWrapperDockerContainerEngine— returnsContainerWaitResponse.StatusCode(was already captured, just discarded)MultiplayerServerManager
Captures the exit code and writes it to both logger and console: