Skip to content

Don't deadlock when stderr is a non-draining pipe#2300

Merged
ten9876 merged 1 commit intomainfrom
auto/Dont-deadlock-when-stderr-is-a-non-draining-pipe
May 3, 2026
Merged

Don't deadlock when stderr is a non-draining pipe#2300
ten9876 merged 1 commit intomainfrom
auto/Dont-deadlock-when-stderr-is-a-non-draining-pipe

Conversation

@ten9876
Copy link
Copy Markdown
Owner

@ten9876 ten9876 commented May 3, 2026

When AetherSDR is launched from OpenDeck (or any other parent that
captures stderr but doesn't drain it — Stream Deck "Run Command",
systemd user services, GUI launchers), the ~64 KB pipe buffer fills
after about 60 KB of debug output and the next fprintf(stderr, ...)
blocks indefinitely. Worse, since #2284 the entire messageHandler
ran under a global QMutex to serialize log-file writes, so once one
thread blocked on stderr every other thread that called qDebug /
qInfo / qWarning queued behind it on the mutex — full-app deadlock,
no log output, threads sitting in futex_wait.

Two changes:

  1. Move the file write into a tighter scope so the mutex is only
    held across the QFile call. The fprintf is outside the mutex —
    even an unexpected stderr stall can no longer block other threads'
    log calls.

  2. Skip the stderr mirror entirely when stderr is not a TTY. CLI
    launches keep their console output; Stream Deck / systemd-user /
    GUI launchers go straight to the log file. The log-file path is
    unchanged in both cases.

isatty / fileno are POSIX; on Windows they live in <io.h> as
_isatty / _fileno. Wrap accordingly.

Co-Authored-By: Claude Opus 4.7 (1M context) [email protected]

When AetherSDR is launched from OpenDeck (or any other parent that
captures stderr but doesn't drain it — Stream Deck "Run Command",
systemd user services, GUI launchers), the ~64 KB pipe buffer fills
after about 60 KB of debug output and the next fprintf(stderr, ...)
blocks indefinitely.  Worse, since #2284 the entire messageHandler
ran under a global QMutex to serialize log-file writes, so once one
thread blocked on stderr every other thread that called qDebug /
qInfo / qWarning queued behind it on the mutex — full-app deadlock,
no log output, threads sitting in futex_wait.

Two changes:

1. Move the file write into a tighter scope so the mutex is only
   held across the QFile call.  The fprintf is outside the mutex —
   even an unexpected stderr stall can no longer block other threads'
   log calls.

2. Skip the stderr mirror entirely when stderr is not a TTY.  CLI
   launches keep their console output; Stream Deck / systemd-user /
   GUI launchers go straight to the log file.  The log-file path is
   unchanged in both cases.

isatty / fileno are POSIX; on Windows they live in <io.h> as
_isatty / _fileno.  Wrap accordingly.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@ten9876 ten9876 requested a review from jensenpat as a code owner May 3, 2026 01:43
@ten9876 ten9876 enabled auto-merge (squash) May 3, 2026 01:43
@ten9876 ten9876 merged commit 1d373b0 into main May 3, 2026
4 checks passed
@ten9876 ten9876 deleted the auto/Dont-deadlock-when-stderr-is-a-non-draining-pipe branch May 3, 2026 01:56
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.

1 participant