Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions .github/workflows/live-shutdown-windows.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: Windows live-session shutdown

on:
pull_request:
paths:
- .github/workflows/live-shutdown-windows.yml
- CMakeLists.txt
- cpp/**
- python/**
- pyproject.toml
- uv.lock
push:
branches: [master, marpaia/17]
workflow_dispatch:

permissions:
contents: read

concurrency:
group: windows-live-shutdown-${{ github.ref }}
cancel-in-progress: true

jobs:
shutdown:
runs-on: windows-2025
timeout-minutes: 20
defaults:
run:
shell: pwsh
env:
CMAKE_BUILD_PARALLEL_LEVEL: "2"
# Exercise the host reference engine without requiring a GPU toolkit.
CMAKE_ARGS: -DCM_ENABLE_METAL=OFF -DCM_ENABLE_CUDA=OFF -DCM_BUILD_TESTS=OFF
steps:
- name: Check out source
uses: actions/checkout@v6
with:
persist-credentials: false

- name: Set up uv
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
enable-cache: true
python-version: "3.12"

- name: Build CPU extension and install locked test dependencies
run: uv sync --locked --group dev

- name: Record platform, shell, interpreter, and backend
run: |
New-Item -ItemType Directory -Force build/shutdown-evidence | Out-Null
$PSVersionTable | Out-File build/shutdown-evidence/platform.txt
[System.Environment]::OSVersion | Out-File -Append build/shutdown-evidence/platform.txt
uv run --no-sync python --version 2>&1 | Tee-Object -Append build/shutdown-evidence/platform.txt
uv run --no-sync microsimulator devices --json | Tee-Object -Append build/shutdown-evidence/platform.txt

- name: Verify Stop, console Ctrl+C, worker draining, and same-port restart
run: >-
uv run --no-sync python -m pytest
python/tests/test_viewer_server.py python/tests/test_viewer_shutdown.py
-v --tb=short --junitxml=build/shutdown-evidence/tests.xml

- name: Upload shutdown evidence
if: ${{ always() }}
uses: actions/upload-artifact@v7
with:
name: windows-live-shutdown-${{ github.sha }}
path: build/shutdown-evidence
if-no-files-found: warn
retention-days: 30
20 changes: 20 additions & 0 deletions docs/protocols/live-viewer-v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@ Rejected commands and model failures return a data-only error:
{ "type": "error", "message": "reason" }
```

Intentional shutdown sends lifecycle notifications before closing the WebSocket with code 1000:

```json
{"type":"session","state":"stopping"}
{"type":"session","state":"stopped"}
```

`stopping` means admission of new work has ended. `stopped` means the active operation has finished and the simulation worker has terminated. Clients should process previously received messages (including asynchronous scene verification) before interpreting the subsequent socket close. A close without `stopped` is still an unexpected disconnect. These messages extend the v1 vocabulary; use a viewer built from the same release as the server.

## Client commands

The vocabulary is closed. Unknown fields are rejected.
Expand All @@ -48,8 +57,19 @@ The vocabulary is closed. Unknown fields are rejected.
{"type":"pause"}
{"type":"reset"}
{"type":"checkpoint"}
{"type":"stop"}
```

`steps` defaults to one and is bounded to 1 through 10,000. Playback advances the configured number of steps per published frame. A step or reset first pauses playback. Disconnecting the final client pauses the simulation.

Reset calls the original server-side model factory again with its original backend, device, seed, parameters, and resume source. Checkpoint writes only to the destination configured when the server starts and atomically replaces that file. It preserves controller state for any runnable model implementing the `SimulationController` protocol, including the legacy compatibility adapter.

## Stop and restart

Stop ends this server process and releases its listening port. It is authenticated through the same token and exact-origin WebSocket upgrade as every other command. The reader handles Stop immediately, including while an earlier command on that same socket is executing. Ordinary commands retain per-client ordering in a bounded queue of 32; additional queued commands receive an error rather than blocking Stop.

Shutdown is cooperative: an in-progress individual simulation step finishes, then the rest of its batch is skipped. An already-running reset, scene capture, or atomic checkpoint write also finishes. There is no timeout that kills the worker in the middle of model state mutation or file replacement. A model operation that never returns will therefore keep the session in `stopping`. Queued operations and newly received Frame, Play, Step, Pause, Reset, and Checkpoint commands are rejected once stopping begins; repeated Stop requests are idempotent. Closing the final browser connection still pauses the session and allows reconnection.

Browser Stop and terminal Ctrl+C share the same worker/socket cleanup path. After `stopped`, the server closes client sockets and its application runner, exits, and releases the port. Launch the next `microsimulator view` command from the terminal and open its newly printed URL; each process has a new token. The old browser keeps its last rendered frame and displays Stopped.

Network delivery has separate deadlines from cooperative model work. Initial frame writes, broadcasts, and lifecycle notifications allow one second per receiver; independent lifecycle deliveries run concurrently. The complete WebSocket close operation, including writing and draining its close frame, also has a one-second deadline. An unresponsive connection is then aborted, discarding queued network bytes so neither its initial-send handler nor the application runner waits indefinitely for a reader. Responsive clients still receive `stopping`, `stopped`, and a normal code-1000 close. A stalled client may miss these notifications and observe an unexpected disconnect; this does not cancel an active simulation operation or interrupt an atomic checkpoint write.
Loading