melt: non-zero exit when interrupted by a signal (#547)#1270
Open
Magnussmari wants to merge 1 commit into
Open
melt: non-zero exit when interrupted by a signal (#547)#1270Magnussmari wants to merge 1 commit into
Magnussmari wants to merge 1 commit into
Conversation
melt caught SIGINT/SIGTERM (and SIGHUP/SIGPIPE on non-Windows) via stop_handler, which sets the producer "done" property for a graceful stop but then let main() return 0 — so a Ctrl-C'd, incomplete render reported success and callers could not detect it. Record the signal in a volatile sig_atomic_t and, after the output is finalized, restore the default handler and re-raise it so the exit status reflects the signal (130 for SIGINT, 143 for SIGTERM, etc.), mirroring the existing abnormal_exit_handler in this file (term_exit then signal(SIG_DFL) then raise). On Windows, surface EXIT_FAILURE. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Member
|
Besides what was already written, why do you want to change it? Is this just fodder for your AI? |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates the melt CLI to return a non-zero exit status when interrupted by termination signals (e.g., Ctrl-C), so calling scripts can reliably detect incomplete/truncated renders (fixes #547).
Changes:
- Record the terminating signal in a
volatile sig_atomic_t(stop_signum) insidestop_handler. - After the consumer stops and output is flushed, restore the default handler and re-raise the signal on non-Windows to produce standard
128 + signumexit codes (Windows returnsEXIT_FAILUREinstead).
Comments suppressed due to low confidence (1)
src/melt/melt.c:56
stop_handlerruns in a signal-handler context, but it callsmlt_properties_set_int(), which is not guaranteed to be async-signal-safe. That can lead to undefined behavior (e.g., deadlocks/crashes if the signal interrupts code holding internal locks). Now thatstop_signumexists, consider making the handler only record the signal (and possibly set a simplesig_atomic_t"stop requested" flag), and have the main/render loop perform the MLT property update in a safe context.
stop_signum = signum;
if (melt) {
mlt_properties properties = MLT_PRODUCER_PROPERTIES(melt);
mlt_properties_set_int(properties, "done", 1);
}
Comment on lines
+1146
to
+1153
| if (stop_signum && !error) { | ||
| #ifndef _WIN32 | ||
| term_exit(); | ||
| signal(stop_signum, SIG_DFL); | ||
| raise(stop_signum); | ||
| #endif | ||
| error = EXIT_FAILURE; // Windows: no re-raise, so surface a non-zero status | ||
| } |
ddennedy
reviewed
Jul 15, 2026
Comment on lines
+1148
to
+1152
| term_exit(); | ||
| signal(stop_signum, SIG_DFL); | ||
| raise(stop_signum); | ||
| #endif | ||
| error = EXIT_FAILURE; // Windows: no re-raise, so surface a non-zero status |
Member
There was a problem hiding this comment.
After further reviewing the referenced issue discussion and considering your use case for headless rendering, I can understand how this might affect you.
Based on both the PR description and the copilot review comment, I think this should be like this instead of the copilot suggestion:
Suggested change
| term_exit(); | |
| signal(stop_signum, SIG_DFL); | |
| raise(stop_signum); | |
| #endif | |
| error = EXIT_FAILURE; // Windows: no re-raise, so surface a non-zero status | |
| abnormal_exit_handler(stop_signum); | |
| #else | |
| error = EXIT_FAILURE; // Windows: no re-raise, so surface a non-zero status | |
| #endif |
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.
Fixes #547.
melttrapsSIGINT/SIGTERM(andSIGHUP/SIGPIPEon non-Windows) withstop_handler, which sets the producerdoneproperty for a graceful stop. The render loop then unwinds andmain()returns0, so a Ctrl-C'd, incomplete render reports success and a calling script cannot tell the output was truncated. This is the case @mzealey raised (drivingmeltfrom a shell script).The fix. Record the signal in a
volatile sig_atomic_t, and once the output file is finalized (aftermlt_consumer_stop), restore the default handler and re-raise so the exit status reflects the signal. This is @marcespie's suggested approach, andraise(sig)is the portable ISO-C equivalent of hiskill(getpid(), sig). It also mirrors theabnormal_exit_handleralready in this file, which doesterm_exit(); signal(signum, SIG_DFL); raise(signum);— including theterm_exit()call, which matters: re-raising bypassesatexit, so without it a Ctrl-C during interactive SDL playback (whereterm_initputs the tty in raw mode) would leave the terminal needingreset.Exit-status breadth. Any trapped termination signal now yields
128 + signuminstead of0:SIGINT130 andSIGTERM143 on all platforms, plusSIGHUP129 andSIGPIPE141 on non-Windows. In particular a broken output pipe now exits 141 instead of 0, which is standard Unix behavior and lets scripts detect truncation. Keeping it uniform (rather than special-casingSIGINT) matchesabnormal_exit_handlerand adds no new flag surface. On Windows the re-raise is skipped andEXIT_FAILUREis returned instead.Unchanged. Normal completion still exits
0, and quitting interactive playback withq/ESC still exits0(that path setsdonedirectly and never setsstop_signum). A genuine consumer fatal error keeps precedence over the signal status via the!errorguard.Testing. Built from source and verified on macOS (Apple Silicon): a render interrupted by
SIGINTnow exits 130 (was 0),SIGTERMexits 143, and a normal render still exits 0.