mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-02 06:50:22 +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:
parent
794a8ce19f
commit
2a0673f99f
78
v2/internal/frontend/desktop/linux/invoke.go
Normal file
78
v2/internal/frontend/desktop/linux/invoke.go
Normal 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
|
||||
}
|
@ -249,11 +249,10 @@ typedef struct JSCallback {
|
||||
char* script;
|
||||
} JSCallback;
|
||||
|
||||
gboolean executeJS(gpointer data) {
|
||||
void executeJS(void *data) {
|
||||
struct JSCallback *js = data;
|
||||
webkit_web_view_run_javascript(js->webview, js->script, NULL, NULL, NULL);
|
||||
free(js->script);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
void extern processMessageDialogResult(char*);
|
||||
@ -265,8 +264,7 @@ typedef struct MessageDialogOptions {
|
||||
int messageType;
|
||||
} MessageDialogOptions;
|
||||
|
||||
gboolean messageDialog(gpointer data) {
|
||||
|
||||
void messageDialog(void *data) {
|
||||
GtkDialogFlags flags;
|
||||
GtkMessageType messageType;
|
||||
MessageDialogOptions *options = (MessageDialogOptions*) data;
|
||||
@ -307,8 +305,6 @@ gboolean messageDialog(gpointer data) {
|
||||
gtk_widget_destroy(dialog);
|
||||
free(options->title);
|
||||
free(options->message);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
void extern processOpenFileResult(void*);
|
||||
@ -333,7 +329,7 @@ void freeFileFilterArray(GtkFileFilter** filters) {
|
||||
free(filters);
|
||||
}
|
||||
|
||||
gboolean opendialog(gpointer data) {
|
||||
void opendialog(void *data) {
|
||||
struct OpenFileDialogOptions *options = data;
|
||||
char *label = "_Open";
|
||||
if (options->action == GTK_FILE_CHOOSER_ACTION_SAVE) {
|
||||
@ -421,7 +417,6 @@ gboolean opendialog(gpointer data) {
|
||||
}
|
||||
gtk_widget_destroy(dlgWidget);
|
||||
free(options->title);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
GtkFileFilter* newFileFilter() {
|
||||
@ -438,12 +433,10 @@ typedef struct RGBAOptions {
|
||||
void *webview;
|
||||
} RGBAOptions;
|
||||
|
||||
gboolean setRGBA(gpointer* data) {
|
||||
void setRGBA(void* data) {
|
||||
RGBAOptions* options = (RGBAOptions*)data;
|
||||
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);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
typedef struct SetTitleArgs {
|
||||
@ -781,7 +774,7 @@ func (w *Window) SetRGBA(r uint8, g uint8, b uint8, a uint8) {
|
||||
a: C.uchar(a),
|
||||
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,
|
||||
script: C.CString(js),
|
||||
}
|
||||
C.ExecuteOnMainThread(C.executeJS, C.gpointer(&jscallback))
|
||||
invokeOnMainThread(func() { C.executeJS(unsafe.Pointer(&jscallback)) })
|
||||
}
|
||||
|
||||
func (w *Window) StartDrag() {
|
||||
@ -902,7 +895,7 @@ func (w *Window) OpenFileDialog(dialogOptions frontend.OpenDialogOptions, multip
|
||||
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) {
|
||||
@ -922,7 +915,7 @@ func (w *Window) MessageDialog(dialogOptions frontend.MessageDialogOptions) {
|
||||
case frontend.WarningDialog:
|
||||
data.messageType = C.int(3)
|
||||
}
|
||||
C.ExecuteOnMainThread(C.messageDialog, C.gpointer(&data))
|
||||
invokeOnMainThread(func() { C.messageDialog(unsafe.Pointer(&data)) })
|
||||
}
|
||||
|
||||
func (w *Window) ToggleMaximise() {
|
||||
|
Loading…
Reference in New Issue
Block a user