mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-02 09:00:38 +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;
|
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() {
|
||||||
|
Loading…
Reference in New Issue
Block a user