Skip to content

Commit 6329e9d

Browse files
authored
fix(linux): re-apply signal handlers periodically to survive JSC lazy init (#5507)
* fix(linux): re-apply signal handlers periodically to survive JSC lazy init WebKit's JavaScriptCore (JSC) lazily installs signal handlers (SIGSEGV for JIT crash recovery, SIGUSR1 for GC thread sync) when JavaScript first executes - not at WebView creation. These handlers are registered without SA_ONSTACK, overwriting Go's handler which requires it. The existing one-shot fix runs before any JS executes, so JSC wipes it out immediately when it initialises. A 50ms periodic timer (100 iterations = 5s) running on the GTK main thread re-applies install_signal_handlers() throughout the JSC initialisation window, ensuring Go's SA_ONSTACK requirement is always satisfied. Fixes #5506 * fix(linux): add SIGUSR1, WEBKIT_LOAD_FINISHED hook, and anchor GTK4 timer Addresses code review feedback on #5507: - Add SIGUSR1 to install_signal_handlers(): JSC uses SIGUSR1 for GC thread synchronisation and installs it without SA_ONSTACK (visible as "Overriding existing handler for signal 10" in stderr). The fix_signal helper only adds SA_ONSTACK to whatever handler is already registered, so this is safe for both Go's and JSC's handler. - Hook WEBKIT_LOAD_FINISHED (v3 GTK4/GTK3) and DomReady (v2): by page-load completion JSC is guaranteed to have initialised and installed all its signal handlers. A targeted call to install_signal_handlers() here provides a deterministic, event-driven fix that complements the periodic timer. - Anchor the GTK4 periodic timer to first WebView creation (sync.Once inside windowNewWebview) rather than appNew(), mirroring the GTK3 pattern. This ensures the 5s coverage window starts when JSC can actually initialise, not before any WebView exists. * fix(linux): immediate install_signal_handlers in GTK4 fixSignalHandlers.Do
1 parent 1133fdc commit 6329e9d

5 files changed

Lines changed: 86 additions & 1 deletion

File tree

v2/internal/frontend/desktop/linux/frontend.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ static void install_signal_handlers()
6565
#if defined(SIGSEGV)
6666
fix_signal(SIGSEGV);
6767
#endif
68+
#if defined(SIGUSR1)
69+
fix_signal(SIGUSR1);
70+
#endif
6871
#if defined(SIGXCPU)
6972
fix_signal(SIGXCPU);
7073
#endif
@@ -79,8 +82,24 @@ static gboolean install_signal_handlers_idle(gpointer data) {
7982
return G_SOURCE_REMOVE;
8083
}
8184
85+
// WebKit's JSC lazily installs signal handlers without SA_ONSTACK when
86+
// JavaScript first executes. This timer re-applies the fix every 50ms
87+
// for the first 5 seconds, covering the JSC initialization window.
88+
static gboolean install_signal_handlers_timeout(gpointer data) {
89+
install_signal_handlers();
90+
int *remaining = (int *)data;
91+
(*remaining)--;
92+
if (*remaining <= 0) {
93+
return G_SOURCE_REMOVE;
94+
}
95+
return G_SOURCE_CONTINUE;
96+
}
97+
8298
static void fix_signal_handlers_after_gtk_init() {
8399
g_idle_add(install_signal_handlers_idle, NULL);
100+
int *remaining = (int *)g_malloc(sizeof(int));
101+
*remaining = 100;
102+
g_timeout_add_full(G_PRIORITY_DEFAULT, 50, install_signal_handlers_timeout, remaining, g_free);
84103
}
85104
86105
*/
@@ -442,6 +461,9 @@ var edgeMap = map[string]uintptr{
442461

443462
func (f *Frontend) processMessage(message string) {
444463
if message == "DomReady" {
464+
// JSC is guaranteed to have initialised by page-load completion, so
465+
// re-apply SA_ONSTACK now to cover any handlers it installed during load.
466+
C.install_signal_handlers()
445467
if f.frontendOptions.OnDomReady != nil {
446468
f.frontendOptions.OnDomReady(f.ctx)
447469
}

v3/pkg/application/linux_cgo.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ void install_signal_handlers(void) {
114114
#if defined(SIGSEGV)
115115
fix_signal(SIGSEGV);
116116
#endif
117+
#if defined(SIGUSR1)
118+
fix_signal(SIGUSR1);
119+
#endif
117120
#if defined(SIGXCPU)
118121
fix_signal(SIGXCPU);
119122
#endif
@@ -122,6 +125,25 @@ void install_signal_handlers(void) {
122125
#endif
123126
}
124127

128+
// WebKit's JSC lazily installs signal handlers without SA_ONSTACK when
129+
// JavaScript first executes. This timer re-applies the fix every 50ms
130+
// for the first 5 seconds, covering the JSC initialization window.
131+
static gboolean install_signal_handlers_timeout(gpointer data) {
132+
install_signal_handlers();
133+
int *remaining = (int *)data;
134+
(*remaining)--;
135+
if (*remaining <= 0) {
136+
return G_SOURCE_REMOVE;
137+
}
138+
return G_SOURCE_CONTINUE;
139+
}
140+
141+
void schedule_signal_handler_fix(void) {
142+
int *remaining = (int *)g_malloc(sizeof(int));
143+
*remaining = 100;
144+
g_timeout_add_full(G_PRIORITY_DEFAULT, 50, install_signal_handlers_timeout, remaining, g_free);
145+
}
146+
125147
// ============================================================================
126148
// Object data helpers
127149
// ============================================================================

v3/pkg/application/linux_cgo.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,10 @@ var (
8181
mainThreadId *C.GThread
8282
)
8383

84-
var registerURIScheme sync.Once
84+
var (
85+
registerURIScheme sync.Once
86+
fixSignalHandlers sync.Once
87+
)
8588

8689
func init() {
8790
gtkSignalToMenuItem = map[uint]*MenuItem{}
@@ -1190,6 +1193,14 @@ func windowNewWebview(parentId uint, gpuPolicy WebviewGpuPolicy) pointer {
11901193
(*[0]byte)(C.onProcessRequest), nil, nil)
11911194
})
11921195

1196+
// Start the periodic signal-handler fix now that a WebView exists and JSC
1197+
// can actually initialise. Anchoring to first webview creation (not appNew)
1198+
// ensures the 5s window covers the JSC lazy-init race window.
1199+
fixSignalHandlers.Do(func() {
1200+
C.install_signal_handlers()
1201+
C.schedule_signal_handler_fix()
1202+
})
1203+
11931204
_ = networkSession
11941205
return pointer(webView)
11951206
}
@@ -1492,6 +1503,9 @@ func handleLoadChanged(wv *C.WebKitWebView, event C.WebKitLoadEvent, data C.uint
14921503
case C.WEBKIT_LOAD_COMMITTED:
14931504
processWindowEvent(C.uint(data), C.uint(events.Linux.WindowLoadCommitted))
14941505
case C.WEBKIT_LOAD_FINISHED:
1506+
// JSC is guaranteed to have initialised by page-load completion, so
1507+
// re-apply SA_ONSTACK now to cover any handlers it installed during load.
1508+
C.install_signal_handlers()
14951509
processWindowEvent(C.uint(data), C.uint(events.Linux.WindowLoadFinished))
14961510
}
14971511
}

v3/pkg/application/linux_cgo.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ void dispatchOnMainThread(unsigned int id);
8383
// ============================================================================
8484

8585
void install_signal_handlers(void);
86+
void schedule_signal_handler_fix(void);
8687

8788
// ============================================================================
8889
// Object data helpers

v3/pkg/application/linux_cgo_gtk3.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,9 @@ static void install_signal_handlers() {
222222
#if defined(SIGSEGV)
223223
fix_signal(SIGSEGV);
224224
#endif
225+
#if defined(SIGUSR1)
226+
fix_signal(SIGUSR1);
227+
#endif
225228
#if defined(SIGXCPU)
226229
fix_signal(SIGXCPU);
227230
#endif
@@ -230,6 +233,25 @@ static void install_signal_handlers() {
230233
#endif
231234
}
232235
236+
// WebKit's JSC lazily installs signal handlers without SA_ONSTACK when
237+
// JavaScript first executes. This timer re-applies the fix every 50ms
238+
// for the first 5 seconds, covering the JSC initialization window.
239+
static gboolean install_signal_handlers_timeout(gpointer data) {
240+
install_signal_handlers();
241+
int *remaining = (int *)data;
242+
(*remaining)--;
243+
if (*remaining <= 0) {
244+
return G_SOURCE_REMOVE;
245+
}
246+
return G_SOURCE_CONTINUE;
247+
}
248+
249+
static void schedule_signal_handler_fix() {
250+
int *remaining = (int *)g_malloc(sizeof(int));
251+
*remaining = 100;
252+
g_timeout_add_full(G_PRIORITY_DEFAULT, 50, install_signal_handlers_timeout, remaining, g_free);
253+
}
254+
233255
static int GetNumScreens(){
234256
return 0;
235257
}
@@ -1419,6 +1441,7 @@ func windowNewWebview(parentId uint, gpuPolicy WebviewGpuPolicy) pointer {
14191441

14201442
fixSignalHandlers.Do(func() {
14211443
C.install_signal_handlers()
1444+
C.schedule_signal_handler_fix()
14221445
})
14231446

14241447
C.save_webview_to_content_manager(unsafe.Pointer(manager), unsafe.Pointer(webView))
@@ -1731,6 +1754,9 @@ func handleLoadChanged(webview *C.WebKitWebView, event C.WebKitLoadEvent, data C
17311754
case C.WEBKIT_LOAD_COMMITTED:
17321755
processWindowEvent(C.uint(data), C.uint(events.Linux.WindowLoadCommitted))
17331756
case C.WEBKIT_LOAD_FINISHED:
1757+
// JSC is guaranteed to have initialised by page-load completion, so
1758+
// re-apply SA_ONSTACK now to cover any handlers it installed during load.
1759+
C.install_signal_handlers()
17341760
processWindowEvent(C.uint(data), C.uint(events.Linux.WindowLoadFinished))
17351761
}
17361762
}

0 commit comments

Comments
 (0)