5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-02 19:31:20 +08:00

[v2, linux] Improve switching to main thread for callbacks (#1392)

Make sure no pointers to the stack are passed to g_idle_add,
because at the time the callback gets executed on the
main thread, the pointer might be invalid.
Go might have reused the stack or grown the stack and the
pointer is invalid.

The concept used on Windows has been ported to Linux.
This commit is contained in:
stffabi 2022-05-31 12:28:37 +02:00 committed by GitHub
parent 794a8ce19f
commit 2a0673f99f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 86 additions and 15 deletions

View File

@ -0,0 +1,78 @@
//go:build linux
// +build linux
package linux
/*
#cgo linux pkg-config: gtk+-3.0
#include <stdio.h>
#include "gtk/gtk.h"
extern gboolean invokeCallbacks(void *);
static inline void triggerInvokesOnMainThread() {
g_idle_add((GSourceFunc)invokeCallbacks, NULL);
}
*/
import "C"
import (
"runtime"
"sync"
"unsafe"
"golang.org/x/sys/unix"
)
var (
m sync.Mutex
mainTid int
dispatchq []func()
)
func invokeOnMainThread(f func()) {
if tryInvokeOnCurrentGoRoutine(f) {
return
}
m.Lock()
dispatchq = append(dispatchq, f)
m.Unlock()
C.triggerInvokesOnMainThread()
}
func tryInvokeOnCurrentGoRoutine(f func()) bool {
m.Lock()
mainThreadID := mainTid
m.Unlock()
runtime.LockOSThread()
defer runtime.UnlockOSThread()
if mainThreadID != unix.Gettid() {
return false
}
f()
return true
}
//export invokeCallbacks
func invokeCallbacks(_ unsafe.Pointer) C.gboolean {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
m.Lock()
if mainTid == 0 {
mainTid = unix.Gettid()
}
q := append([]func(){}, dispatchq...)
dispatchq = []func(){}
m.Unlock()
for _, v := range q {
v()
}
return C.G_SOURCE_REMOVE
}

View File

@ -249,11 +249,10 @@ typedef struct JSCallback {
char* script; char* script;
} JSCallback; } JSCallback;
gboolean executeJS(gpointer data) { void executeJS(void *data) {
struct JSCallback *js = data; struct JSCallback *js = data;
webkit_web_view_run_javascript(js->webview, js->script, NULL, NULL, NULL); webkit_web_view_run_javascript(js->webview, js->script, NULL, NULL, NULL);
free(js->script); free(js->script);
return G_SOURCE_REMOVE;
} }
void extern processMessageDialogResult(char*); void extern processMessageDialogResult(char*);
@ -265,8 +264,7 @@ typedef struct MessageDialogOptions {
int messageType; int messageType;
} MessageDialogOptions; } MessageDialogOptions;
gboolean messageDialog(gpointer data) { void messageDialog(void *data) {
GtkDialogFlags flags; GtkDialogFlags flags;
GtkMessageType messageType; GtkMessageType messageType;
MessageDialogOptions *options = (MessageDialogOptions*) data; MessageDialogOptions *options = (MessageDialogOptions*) data;
@ -307,8 +305,6 @@ gboolean messageDialog(gpointer data) {
gtk_widget_destroy(dialog); gtk_widget_destroy(dialog);
free(options->title); free(options->title);
free(options->message); free(options->message);
return G_SOURCE_REMOVE;
} }
void extern processOpenFileResult(void*); void extern processOpenFileResult(void*);
@ -333,7 +329,7 @@ void freeFileFilterArray(GtkFileFilter** filters) {
free(filters); free(filters);
} }
gboolean opendialog(gpointer data) { void opendialog(void *data) {
struct OpenFileDialogOptions *options = data; struct OpenFileDialogOptions *options = data;
char *label = "_Open"; char *label = "_Open";
if (options->action == GTK_FILE_CHOOSER_ACTION_SAVE) { if (options->action == GTK_FILE_CHOOSER_ACTION_SAVE) {
@ -421,7 +417,6 @@ gboolean opendialog(gpointer data) {
} }
gtk_widget_destroy(dlgWidget); gtk_widget_destroy(dlgWidget);
free(options->title); free(options->title);
return G_SOURCE_REMOVE;
} }
GtkFileFilter* newFileFilter() { GtkFileFilter* newFileFilter() {
@ -438,12 +433,10 @@ typedef struct RGBAOptions {
void *webview; void *webview;
} RGBAOptions; } RGBAOptions;
gboolean setRGBA(gpointer* data) { void setRGBA(void* data) {
RGBAOptions* options = (RGBAOptions*)data; RGBAOptions* options = (RGBAOptions*)data;
GdkRGBA colour = {options->r / 255.0, options->g / 255.0, options->b / 255.0, options->a / 255.0}; GdkRGBA colour = {options->r / 255.0, options->g / 255.0, options->b / 255.0, options->a / 255.0};
webkit_web_view_set_background_color(WEBKIT_WEB_VIEW(options->webview), &colour); webkit_web_view_set_background_color(WEBKIT_WEB_VIEW(options->webview), &colour);
return G_SOURCE_REMOVE;
} }
typedef struct SetTitleArgs { typedef struct SetTitleArgs {
@ -781,7 +774,7 @@ func (w *Window) SetRGBA(r uint8, g uint8, b uint8, a uint8) {
a: C.uchar(a), a: C.uchar(a),
webview: w.webview, webview: w.webview,
} }
C.ExecuteOnMainThread(C.setRGBA, C.gpointer(&data)) invokeOnMainThread(func() { C.setRGBA(unsafe.Pointer(&data)) })
} }
@ -840,7 +833,7 @@ func (w *Window) ExecJS(js string) {
webview: w.webview, webview: w.webview,
script: C.CString(js), script: C.CString(js),
} }
C.ExecuteOnMainThread(C.executeJS, C.gpointer(&jscallback)) invokeOnMainThread(func() { C.executeJS(unsafe.Pointer(&jscallback)) })
} }
func (w *Window) StartDrag() { func (w *Window) StartDrag() {
@ -902,7 +895,7 @@ func (w *Window) OpenFileDialog(dialogOptions frontend.OpenDialogOptions, multip
data.defaultDirectory = C.CString(dialogOptions.DefaultDirectory) data.defaultDirectory = C.CString(dialogOptions.DefaultDirectory)
} }
C.ExecuteOnMainThread(C.opendialog, C.gpointer(&data)) invokeOnMainThread(func() { C.opendialog(unsafe.Pointer(&data)) })
} }
func (w *Window) MessageDialog(dialogOptions frontend.MessageDialogOptions) { func (w *Window) MessageDialog(dialogOptions frontend.MessageDialogOptions) {
@ -922,7 +915,7 @@ func (w *Window) MessageDialog(dialogOptions frontend.MessageDialogOptions) {
case frontend.WarningDialog: case frontend.WarningDialog:
data.messageType = C.int(3) data.messageType = C.int(3)
} }
C.ExecuteOnMainThread(C.messageDialog, C.gpointer(&data)) invokeOnMainThread(func() { C.messageDialog(unsafe.Pointer(&data)) })
} }
func (w *Window) ToggleMaximise() { func (w *Window) ToggleMaximise() {