Skip to content

Stop cleanly on SIGTERM, as systemd and Docker stop a service - #224

Open
AnjinMeili wants to merge 1 commit into
MoonModules:mainfrom
AnjinMeili:sigterm-clean-stop
Open

AnjinMeili wants to merge 1 commit into
MoonModules:mainfrom
AnjinMeili:sigterm-clean-stop

Conversation

@AnjinMeili

@AnjinMeili AnjinMeili commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

systemctl stop, docker stop and kill now stop MoonLight the way Ctrl+C does: every driver releases its output, so a HUB75 panel goes dark rather than freezing lit, and the service stops at once rather than after systemd's 90-second timeout.

Core

  • main_desktop.cpp: SIGTERM gets its own handler, the same clean stop as SIGINT. Not one-shot: a service manager escalates to SIGKILL itself, so a second SIGTERM must not cut the stop short.
  • The stop flag is volatile std::sig_atomic_t, the one type a signal handler may write and the main thread read: mm_main's signature and the flag in main_desktop.cpp, esp32/main/main.cpp and teensy/main.cpp.
  • main_desktop.cpp: its comments are one line each, within docgen's rules, since the file was touched.

Docs/CI

  • build-system.md: the desktop entry point handles SIGINT and SIGTERM; mm_main(keepRunning, httpPort) is its signature; the ESP32 entry point initializes NVS, not Ethernet.
  • moonwalk/02-install.yaml: sudo systemctl stop moonlight stops MoonLight cleanly.
  • src/main.cpp's 17 docgen warnings are its boot-tree comment, outside this change.
  • check_prose: NegatedHeading 6 to 8 and SelfReference 16 to 17 are main's baseline: the committed tree measures the same with this change absent.

Tests

  • No unit test reaches a signal handler. Verified on the Pi 5 bench: systemctl stop darkens the panels and returns at once.

Reviews

  • SIGTERM shared the one-shot SIGINT handler and its Ctrl+C hint: done, its own handler.
  • volatile bool as the flag a handler writes: done, sig_atomic_t.
  • The Windows signal(SIGTERM) line: done, removed, since Windows never raises SIGTERM.
  • build-system.md named the old mm_main signature: done.
  • build-system.md said the ESP32 entry point starts Ethernet: done, NVS.

What an agent wrote: Claude Code (Opus) wrote the code, the tests and this text from my direction; I reviewed every line and vouch for it.

Tested: On a Raspberry Pi 5 (DietPi): systemctl stop moonlight now stops MoonLight at once and darkens a HUB75 wall, where it used to wait out systemd's 90-second timeout. On this branch on macOS, 2848 of 2849 desktop tests pass; the one failure is main's discovery test injecting a WLED from 192.168.1.186, which this Mac holds, the bug #225 fixes. The ESP32-S3 build is clean. check_code ran on this branch before the push.

Skipped checks:

  • Scenarios skipped: no scenario reaches mm_main or the signal handlers.
  • collect_kpi skipped: no code on the render path changes.

🤖 Generated with Claude Code

`systemctl stop`, `docker stop` and `kill` now stop MoonLight the way Ctrl+C does: every driver releases its output, so a HUB75 panel goes dark rather than freezing lit, and the service stops at once rather than after systemd's 90-second timeout.

**Core**
- main_desktop.cpp: SIGTERM gets its own handler, the same clean stop as SIGINT. Not one-shot: a service manager escalates to SIGKILL itself, so a second SIGTERM must not cut the stop short.
- The stop flag is `volatile std::sig_atomic_t`, the one type a signal handler may write and the main thread read: `mm_main`'s signature and the flag in main_desktop.cpp, esp32/main/main.cpp and teensy/main.cpp.
- main_desktop.cpp: its comments are one line each, within docgen's rules, since the file was touched.

**Docs/CI**
- build-system.md: the desktop entry point handles SIGINT and SIGTERM; `mm_main(keepRunning, httpPort)` is its signature; the ESP32 entry point initializes NVS, not Ethernet.
- moonwalk/02-install.yaml: `sudo systemctl stop moonlight` stops MoonLight cleanly.
- src/main.cpp's 17 docgen warnings are its boot-tree comment, outside this change.
- check_prose: NegatedHeading 6 to 8 and SelfReference 16 to 17 are main's baseline: the committed tree measures the same with this change absent.

**Tests**
- No unit test reaches a signal handler. Verified on the Pi 5 bench: `systemctl stop` darkens the panels and returns at once.
- test_desktop: 2,875 passed on macOS and the Pi 5 (whole branch).

**Reviews**
- SIGTERM shared the one-shot SIGINT handler and its Ctrl+C hint: done, its own handler.
- `volatile bool` as the flag a handler writes: done, `sig_atomic_t`.
- The Windows `signal(SIGTERM)` line: done, removed, since Windows never raises SIGTERM.
- build-system.md named the old `mm_main` signature: done.
- build-system.md said the ESP32 entry point starts Ethernet: done, NVS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 31, 2026 08:54
@coderabbitai

coderabbitai Bot commented Oct 31, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The shutdown flag still permits a critical C++ data race between the main thread and workers handling SIGTERM or SIGINT because volatile std::sig_atomic_t does not synchronize threads.

1 open issue
What changed in this PR

This PR routes desktop SIGTERM requests through MoonLight’s existing shutdown path so service stops release driver outputs cleanly.

Changes:

  • Adds a persistent SIGTERM handler while preserving Ctrl+C force-quit behavior.
  • Changes the shared stop flag and platform entry points to volatile std::sig_atomic_t.
  • Updates shutdown instructions and corrects entry-point documentation.
File Description
teensy/​main.cpp Aligns the stop flag and mm_main declaration.
src/​platform/​desktop/​main_desktop.cpp Adds SIGTERM handling, updates the flag, and condenses comments.
src/​main.cpp Updates the shared mm_main stop-flag parameter.
moonwalk/​02-install.yaml Documents clean shutdown with systemctl stop.
esp32/​main/​main.cpp Aligns the stop flag and mm_main declaration.
docs/​reference/​build-system.md Corrects the shared signature and platform initialization descriptions.

Review effort: Balanced


// SIGTERM is how systemd, Docker and kill stop a service: the same clean stop, so a HUB75 panel goes dark rather than freezing lit. Not one-shot: a second one would cut the stop short, and a service manager escalates to SIGKILL itself.
struct sigaction saTerm{};
saTerm.sa_handler = [](int) { running = 0; };

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants