Run two or more Webview instances

I’m trying to write a Go application that can display multiple Webviews ( github.com/webview/webview ) that host small Javascript applications, think maybe of a stick note application that displays multiple small notes. I use Linux, Windows is not a target platform for me. I looked at gotk as well, but it doesn’t seem to provide WebKit.Webview yet. Also, I like how you can call Javascript functions with Webview and Bind Go function so that they can be used from Javascript.

I could achieve what I want by running multiple instances of the same Go program, i.e. one for each Webview. I could even have a simple ‘server’ in the middle (also a tiny Go program) that launches these Webviews, serves as a persistence layer, provides static assets and maybe even work as a message bus of sorts. This seems interesting (would be an interesting project though) but I would rather have just one Go application with a dedicated channel for each Webview.

So I thought I could just try to launch two Webview instances from the same Go program. My first idea was it could be possible by simply using go routines, something like this:

// Code does not work
package main

import (
	"fmt"
	"github.com/webview/webview"
)

func runWebview(url string) {
	w := webview.New(true)
	defer w.Destroy()
	w.SetTitle("Minimal webview example")
	w.SetSize(800, 600, webview.HintNone)
	w.Navigate(url)
	w.Run()
}

func main() {
	go runWebview("https://en.m.wikipedia.org/wiki/Main_Page")
	go runWebview("https://www.reddit.com")
	var input string
	fmt.Scanln(&input)
}

This however does not work, I get an invalid pointer exception. I guess that is because both Webviews are connected to the same main program. There is also webview.NewWindow(debug bool, window unsafe.Pointer) which can be passed a pointer to a GtkWindow but I have no idea how I could use this.

Would it be more viable to use Webkit.Webview directly, maybe extending gotk (but I have no idea how to could do this)? Honestly, I was happy to abstract away GTK. I know there is also Wails but I only had a brief look at it. If Wails can launch multiple Webviews I could maybe have a closer look.

Any ideas? I’m happy to hear your thoughts about this.

What if you put a call to runtime.LockOSThread() as the first line in runWebview?

Thanks for the suggestion. Unfortunately, it does not make a difference. Btw, if I only run one Webview this way (in a go routine, with or without OSLockThread) it does work as expected.

This issue seems to imply that running multiple webviews on their own threads is supported. What exactly is that nil pointer error you get when you try to use multiple web views? Perhaps the stack trace (if any) can tell us something.

If it doesn’t, my next step would be to try it without Go to see if the issue is in the Go code or if it’s in the C code.

Hm, actually I get different error messages even with I rerun the same binary. It’s stuff like

realloc(): invalid old size                                                                                                                                                                    
SIGABRT: abort                                                                                                                                                                                 
PC=0x7ffb0ffafd22 m=3 sigcode=18446744073709551610          

or

realloc(): invalid old size                                                                                                                                                                    
SIGABRT: abort                                                                                                                                                                                 
PC=0x7ffb0ffafd22 m=3 sigcode=18446744073709551610          

But the root cause seems to be the error afterwards: runtime: unknown pc 0xsomeadress, followed by a long hex dump and then the stacktrace.

free(): invalid pointer
SIGABRT: abort
PC=0x7fa47fc65d22 m=3 sigcode=18446744073709551610

goroutine 0 [idle]:
runtime: unknown pc 0x7fa47fc65d22
stack: frame={sp:0x7fa44fffe4d0, fp:0x0} stack=[0x7fa44f7fef50,0x7fa44fffeb50)
0x00007fa44fffe3d0:  0x00007fa454202b20  0x00007fa48291c3d4 
0x00007fa44fffe3e0:  0x00000000eae2d7a3  0x00007fa44fffe434 

... goes on like this for some lines ...

0x00007fa44fffe5b0:  0xffffffffffffffff  0xffffffffffffffff 
0x00007fa44fffe5c0:  0xffffffffffffffff  0xffffffffffffffff 

goroutine 7 [syscall]:
runtime.cgocall(0x4a87a0, 0xc00006aee8)
	/usr/lib/go/src/runtime/cgocall.go:156 +0x5c fp=0xc00006aec0 sp=0xc00006ae88 pc=0x40851c
github.com/webview/webview._Cfunc_webview_create(0x1, 0x0)
	_cgo_gotypes.go:137 +0x4d fp=0xc00006aee8 sp=0xc00006aec0 pc=0x4a6ecd
github.com/webview/webview.NewWindow.func1(0x1, 0x0)
	/home/user/go/pkg/mod/github.com/webview/webview@v0.0.0-20210330151455-f540d88dde4e/webview.go:162 +0x47 fp=0xc00006af20 sp=0xc00006aee8 pc=0x4a7567
github.com/webview/webview.NewWindow(0x0, 0x0)
	/home/user/go/pkg/mod/github.com/webview/webview@v0.0.0-20210330151455-f540d88dde4e/webview.go:162 +0x3d fp=0xc00006af58 sp=0xc00006af20 pc=0x4a74bd
github.com/webview/webview.New(...)
	/home/user/go/pkg/mod/github.com/webview/webview@v0.0.0-20210330151455-f540d88dde4e/webview.go:152
main.runWebview({0x4cce58, 0x16})
	/home/user/webview/example/two_instances.go:10 +0x45 fp=0xc00006afc0 sp=0xc00006af58 pc=0x4a82e5
main.main·dwrap·3()
	/home/user/webview/example/two_instances.go:24 +0x2a fp=0xc00006afe0 sp=0xc00006afc0 pc=0x4a854a
runtime.goexit()
	/usr/lib/go/src/runtime/asm_amd64.s:1581 +0x1 fp=0xc00006afe8 sp=0xc00006afe0 pc=0x4623e1
created by main.main
	/home/user/webview/example/two_instances.go:24 +0x7f

goroutine 1 [syscall, locked to thread]:
syscall.Syscall(0x0, 0x0, 0xc0000a81d0, 0x1)
	/usr/lib/go/src/syscall/asm_linux_amd64.s:20 +0x5
syscall.read(0xc000094060, {0xc0000a81d0, 0xc00007ba70, 0xc00007ba70})
	/usr/lib/go/src/syscall/zsyscall_linux_amd64.go:687 +0x4d
syscall.Read(...)
	/usr/lib/go/src/syscall/syscall_unix.go:189
internal/poll.ignoringEINTRIO(...)
	/usr/lib/go/src/internal/poll/fd_unix.go:582
internal/poll.(*FD).Read(0xc000094060, {0xc0000a81d0, 0x1, 0x4})
	/usr/lib/go/src/internal/poll/fd_unix.go:163 +0x285
os.(*File).read(...)
	/usr/lib/go/src/os/file_posix.go:32
os.(*File).Read(0xc000010010, {0xc0000a81d0, 0xc00, 0xc00})
	/usr/lib/go/src/os/file.go:119 +0x5e
io.ReadAtLeast({0x4ebf60, 0xc000010010}, {0xc0000a81d0, 0x1, 0x4}, 0x1)
	/usr/lib/go/src/io/io.go:328 +0x9a
io.ReadFull(...)
	/usr/lib/go/src/io/io.go:347
fmt.(*readRune).readByte(0xc0000a81b0)
	/usr/lib/go/src/fmt/scan.go:321 +0x45
fmt.(*readRune).ReadRune(0xc0000a81b0)
	/usr/lib/go/src/fmt/scan.go:337 +0xa5
fmt.(*ss).ReadRune(0xc000094180)
	/usr/lib/go/src/fmt/scan.go:189 +0x59
fmt.(*ss).getRune(0x0)
	/usr/lib/go/src/fmt/scan.go:211 +0x1d
fmt.(*ss).SkipSpace(0xc000094180)
	/usr/lib/go/src/fmt/scan.go:422 +0x25
fmt.(*ss).convertString(0xc00007bdb0, 0x76)
	/usr/lib/go/src/fmt/scan.go:828 +0x4e
fmt.(*ss).scanOne(0xc000094180, 0x10001, {0x4b49e0, 0xc000012270})
	/usr/lib/go/src/fmt/scan.go:1012 +0x379
fmt.(*ss).doScan(0xc000094180, {0xc00007bf60, 0x1, 0x7fa47b6c5438})
	/usr/lib/go/src/fmt/scan.go:1073 +0x151
fmt.Fscanln({0x4ebf60, 0xc000010010}, {0xc00007bf60, 0x1, 0x1})
	/usr/lib/go/src/fmt/scan.go:132 +0xac
fmt.Scanln(...)
	/usr/lib/go/src/fmt/scan.go:70
main.main()
	/home/user/webview/example/two_instances.go:26 +0xc9

goroutine 6 [syscall]:
github.com/webview/webview._Cfunc_webview_create(0x1, 0x0)
	_cgo_gotypes.go:137 +0x4d
github.com/webview/webview.NewWindow.func1(0x1, 0x0)
	/home/user/go/pkg/mod/github.com/webview/webview@v0.0.0-20210330151455-f540d88dde4e/webview.go:162 +0x47
github.com/webview/webview.NewWindow(0x0, 0x0)
	/home/user/go/pkg/mod/github.com/webview/webview@v0.0.0-20210330151455-f540d88dde4e/webview.go:162 +0x3d
github.com/webview/webview.New(...)
	/home/user/go/pkg/mod/github.com/webview/webview@v0.0.0-20210330151455-f540d88dde4e/webview.go:152
main.runWebview({0x4d0b2c, 0x29})
	/home/user/webview/example/two_instances.go:10 +0x45
created by main.main
	/home/user/webview/example/two_instances.go:23 +0x4c

rax    0x0
rbx    0x7fa44ffff640
rcx    0x7fa47fc65d22
rdx    0x0
rdi    0x2
rsi    0x7fa44fffe4d0
rbp    0x7fa47fdeb8f0
rsp    0x7fa44fffe4d0
r8     0x0
r9     0x7fa44fffe4d0
r10    0x8
r11    0x246
r12    0x7fa44fffe750
r13    0x1000
r14    0x10
r15    0x7fa48687d000
rip    0x7fa47fc65d22
rflags 0x246
cs     0x33
fs     0x0
gs     0x0

I found some Go issues specifically for the runtime: unknown pc and indeed it seems to be a problem with how Go calls C function. This goes a little deeper than I anticipated. I don’t understand the problem very well.

These are the same errors. I’m not sure what the problem is because I cannot find the source code to the actual webview.dll. Perhaps you could open an issue here: Issues · webview/webview · GitHub

Oops, you’re right! I recently have a problem with Vim not yanking to the system clipboard anymore, didn’t mean to post the same error message twice.

I think the webview issue tracker is a good option, thanks for the idea. I thought I was completely on the wrong track but it seems to be more involved.

set clipboard=unnamed

When the “unnamed” string is included in the ‘clipboard’ option, the unnamed
register is the same as the "* register. Thus you can yank to and paste the
selection without prepending "* to commands.