WebView: fatal: morestack on g0
pennersr opened this issue ยท 16 comments
I was trying out the webview and ran into an issue using the following code:
package main
import (
. "github.com/lxn/walk/declarative"
)
func main() {
m := MainWindow{
Title: "Webtest",
MinSize: Size{600, 400},
Layout: VBox{},
Children: []Widget{
WebView{
URL: "https://www.google.com",
},
},
}
m.Run()
}
Try searching for "what is my browser" using the embedded search page. Then, click on the "https://www.whatismybrowser.com" link that is likely to be the 1st search result. Alternatively, you can change the URL in the source directly to point to whatismybrowser. The page never appears, instead an error message is printed thousands of time on the console and the application crashes:
fatal: morestack on g0
fatal: morestack on g0
fatal: morestack on g0
fatal: morestack on g0
...
WebView
is very limited in functionality and, especially on 64 bit Go, unstable. In general I would advise against using walk on top of 64 bit Go. I'm running into problems with one of my projects that look like a stack overflow, resulting in the same error message you have reported here. It seems like Go limits the size of the main os thread it starts on to just 128K, which quickly get exhausted with deeply nested callbacks, even more so with 64 bit pointers all over the place.
It seems like Go limits the size of the main os thread it starts on to just 128K
@lxn Do you mean the size of stack or the number of threads? I suppose you mean the stack size.
As far I know, all goroutines, including the main
one, do NOT use the system stack created by the OS.
And the stack size of each normal goroutine starts small and growing when needed. The problem you encounter is probably a Go runtime bug.
@lxn Oh, I see. BTW I can build & run the reported program on my win10 64bit machine. I built it with Go 1.8.1.
There may actually be a really hackish workaround for this (and the 64-bit WebView
problems in general). Perhaps @lxn can comment as to whether or not he's gone down this path.
The issue with the limited stack size was actually reported to the Go project before (golang/go#8233), but it was retracted because apparently the author found a workaround.
However, there's a somewhat related issue (golang/go#9457), where programs using the syscall interface to load and run DLLs weren't being given enough stack space because they weren't importing runtime/cgo
(this is the indicator that the Go linker uses to determine if it should grant a larger stack space) (see this comment). Adding a blank import of runtime/cgo
(import _ "runtime/cgo"
) will cause the linker to give the executable a more standard 2 MB of stack space on 64-bit Windows (1 MB on 32-bit Windows).
I tried adding such an import to the sample program above to trick the linker into giving the executable more stack space and it seemed to work (crashed before, not after, although there are still some scripting errors).
There's just one hitch, which is that CGO on Windows doesn't understand the -H windowsgui
build mode, so you have to build it as a console program and deal with the annoying console window. This problem was brought up on the golang-nuts mailing list last month, although it doesn't look like any action has been taken on it yet (https://groups.google.com/forum/#!topic/golang-nuts/4rnCvFKTYgs/discussion).
But, it's not a huge issue, because if you install Visual Studio (the community version is free), you can use the editbin.exe
program to manually change the executable subsystem after linking (http://stackoverflow.com/a/2435907/2829001). There are other ways to do this, though this is the most canonical. In any case, it seems to work on Go executables - I think it just changes some bit in the PE header.
As far as the scripting errors are concerned, I'm not sure, but I think that's probably related to the OLE/COM callbacks or FEATURE_*
keys that control the WebBrowser control.
You're very welcome - I'm glad to hear that it's a new avenue of approach. I'm not sure if it's the panacea you've been searching for with regards to WebView
controls on 64-bit Windows (I know you've been trying to figure out the problems there for years), but maybe this can help with some of the issues.
I'm also not sure if there's anything here worth reporting back to the Go team in terms of fixable issues. I think maybe CGO support for the windowsgui
build mode is worth opening an issue, but the Windows Go team is stretched thin in general and may not get to it for a while. Given that building walk
-based applications already requires a few extra steps with the manifest, perhaps one more step to manually change the subsystem (if using this runtime/cgo
trick) isn't so bad.
At least for me this solution would suffice.
Right now I have some other ffi related trouble though. I have an experimental walk branch with support to decorate widgets with graphics effects like border glow and drop shadow, implemented using Direct2D. The problem is, Direct2D has a few important methods that have a float
parameter, but the syscall
package does not support that. For this particular task it was possible to work around this, but it would rule out replacing the current GDI based Canvas
implementation with one using Direct2D (or GDI+, which also uses float parameters).
The problem has been reported to the Go team in the past though I can't find any link now, but IIRC the resolution was to use CGO instead of the syscall package...
That's a bit of a shame - adding a "real" CGO dependency that required the MinGW toolchain would be a bit heavy. Although I guess you could hide that API behind a cgo
build constraint? Go's built-in DLL-loading "syscall" interface on Windows is one of it's best features IMHO - it's just a shame that it supports a limited subset of the ABI.
Yes, it's sad. I would really like to avoid CGO.
It looks like part of this problem is going to be fixed in Go 1.9 thanks to @kjk notifying the Go developers with golang/go#20975. Go will default to 2 MB stacks for 64-bit Windows in 1.9 and then also default to 1 MB stacks for 32-bit Windows in 1.10. The latter change is being pushed back so they can evaluate the effects of the increased memory pressure during the Go 1.10 development cycle. But since the webview problems only really manifest on 64-bit systems anyway, this problem may be largely solved.
Even with that fix, web view on 64bit is very unstable.
When visiting https://vox.com, webview sample crashes for me always with a stack overflow in runtime even if I give it ridiculous amounts of stack (I tried up to 16 MB).
This might be a bug in the runtime or a bug in webview implementation or a combination (the latter triggering the former).
Yeah, there are definitely still some issues. It's impressive that @lxn managed to make the thing work at all given the COM/OLE trickery involved (especially without resorting to CGO). I think there are a number of other potential areas that warrant investigation. I'm hoping to look at this in the next 1-2 weeks, probably in a separate package to get as clean an implementation as possible.
First, there are a lot of registry keys and configuration flags that can be set on a per-process basis to regulate the webview's behavior (e.g. stuff like enabling GPU rendering). These can be set in an ephemeral fashion so that they only span the lifetime of the process. Here's just a small example of some of these (this is in the context of the .NET WebBrowser
control, but that's the same underlying COM object).
Second, there may also be some other COM interfaces that the webview is relying on being able to call back into for certain things (like setting script execution policy). There's a bunch of different interfaces that the host application can implement to customize the webview behavior. Unfortunately the documentation for a lot of this stuff is becoming increasingly buried on MSDN.
I'll update here if I make any progress.
After thinking a bit more about your comment (and re-reading golang/go#20975), I'm really starting to doubt the role of stack size even more. 16 MB is sort of an insane amount of stack - even Visual Studio doesn't create stack sizes that large when compiling C/C++ code, and there's loads of C/C++ code out there using IWebBrowser2
. I think the assessment of infinite recursion is probably close to the truth. The webview control certainly isn't allocating its internal resources on the stack, so the size and complexity of the webpage should really make little difference. I think what's far more likely is that either there's some unhandled COM method being called or a message being dispatched in a way that causes infinite recursion.
Although Go 1.9 hasn't fixed the "morestack" issue (which I'm fairly certain is due to thread-local storage not being supported in purely syscall-based C FFI), it seems they did fix the issue with windowsgui
build mode conflicting with CGO. So you can now use the import _ "runtime/cgo"
trick without having to use editbin.exe
to change the executable subsystem. This at least removes the need for Visual Studio to work around the issue.
This issue is most likely fixed in go 1.11 (see golang/go#20975)