fix(linux): replace SIGUSR1/SIGUSR2 with SIGRTMIN+1/+2 to avoid WebKit GC conflict#1267
fix(linux): replace SIGUSR1/SIGUSR2 with SIGRTMIN+1/+2 to avoid WebKit GC conflict#1267muriloime wants to merge 2 commits into
Conversation
…t GC conflict WebKitGTK (embedded by Tauri as the webview engine) uses SIGUSR1 internally for JavaScriptCore's garbage collector "stop the world" thread suspension via pthread_kill. Because signal-hook's self-pipe file descriptor is inherited by WebKit child processes on fork, those intra-process GC signals leak back into the parent Handy process and are misinterpreted as user-triggered transcription. This causes ghost transcription activations at predictable intervals after every startup: ~2 min (first JSC full GC, heap ~268 KB) and ~7 min later (second GC after heap compaction, ~80 KB), with the exact timing determined by JSC's GC timer formula in GCActivityCallback.cpp: delay = lastGCLength(10ms) / ((heapMB) × 0.0003125) Fix: switch to POSIX real-time signals SIGRTMIN+1 (transcribe_with_post_process) and SIGRTMIN+2 (transcribe), which WebKit/JSC does not use. Migration note: users with `pkill -USR1 handy` / `pkill -USR2 handy` shortcuts should switch to `handy --toggle-post-process` / `handy --toggle-transcription` (D-Bus path, no raw signals needed). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
I don't think this really can be merged without disrupting quite a lot of people Maybe SIGUSR1 can be changed but otherwise this is a breaking change |
|
I will try to come up with another approach then |
…mpat Keep SIGUSR2 → transcribe unchanged (backward compatible for existing shortcuts). Only change SIGUSR1 → SIGRTMIN+1 for transcribe_with_post_process, since SIGUSR1 is the signal WebKit/JSC repurposes for GC thread suspension. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Updated. SIGUSR2 is kept untouched — only SIGUSR1 is replaced with SIGRTMIN+1, since SIGUSR1 is specifically the signal WebKit/JSC repurposes for GC. SIGUSR2 users are unaffected. @cjpais do you think this less intrusive approach is ok? |
|
@cjpais Forgot to mention, but of course I stopped seing Handy automatically starting at 122s , and 409s on each Handy start. It was quite annoying ( weirdly this happened only on 1 of my two linux boxes) |
|
Thanks for the info, I will take a look and think more on this soon. Just give me some time, I am working on some parallel stuff which is higher priority for me at the moment |
Problem
On Linux, Handy triggers ghost transcriptions at predictable intervals after every startup:
This happens even with no keyboard input.
Root Cause
WebKitGTK (embedded by Tauri as the webview engine on Linux) uses SIGUSR1 internally for JavaScriptCore's garbage collector "stop the world" mechanism — it suspends GC threads via
pthread_kill(thread, SIGUSR1).Because
signal-hook's self-pipe file descriptor is inherited by WebKit child processes on fork, those intra-process GC signals leak back into the parent Handy process and are misinterpreted as user-triggered transcription commands.The exact timing is determined by JSC's GC timer formula in
GCActivityCallback.cpp:The double signal (always two SIGUSR1 in the same second) comes from the two
WebKitWebProcesschildren both running JSC GC simultaneously.Fix
Switch from
SIGUSR1/SIGUSR2to POSIX real-time signalsSIGRTMIN+1andSIGRTMIN+2, which WebKit/JSC does not use. Real-time signals are specifically designed for application-level IPC and avoid conflicts with standard signals repurposed by system libraries.Changes:
src-tauri/Cargo.toml: addlibc = "0.2"under[target.'cfg(unix)'.dependencies]src-tauri/src/lib.rs: registerSIGRTMIN+1/SIGRTMIN+2instead ofSIGUSR1/SIGUSR2src-tauri/src/signal_handle.rs: update handler to match on the runtime RT signal valuesMigration
Users with raw signal shortcuts (e.g.
pkill -USR1 handy/pkill -USR2 handy) should switch to the CLI flags instead:These already use D-Bus via
tauri-plugin-single-instanceand require no signal at all.Evidence
Handy log showing the ghost triggers every session, exactly 122s and 409s after startup:
Consistent across 10+ sessions in the log history.
🤖 Generated with Claude Code