Skip to content

misc/wasm: deadlock from http.Get in syscall/js.FuncOf #37136

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
QFleet opened this issue Feb 8, 2020 · 6 comments
Closed

misc/wasm: deadlock from http.Get in syscall/js.FuncOf #37136

QFleet opened this issue Feb 8, 2020 · 6 comments
Labels
arch-wasm WebAssembly issues FrozenDueToAge NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.

Comments

@QFleet
Copy link

QFleet commented Feb 8, 2020

What version of Go are you using (go version)?

$ go version
go1.13.6

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
set GO111MODULE=on
set GOARCH=wasm
set GOBIN=
set GOCACHE=C:\Users\Administrator\AppData\Local\go-build
set GOENV=C:\Users\Administrator\AppData\Roaming\go\env
set GOEXE=
set GOFLAGS=
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GONOPROXY=
set GONOSUMDB=
set GOOS=js
set GOPATH=C:\Users\Administrator\go
set GOPRIVATE=
set GOPROXY=https://goproxy.cn,direct
set GOROOT=C:\Go
set GOSUMDB=sum.golang.org
set GOTMPDIR=
set GOTOOLDIR=C:\Go\pkg\tool\windows_amd64
set GCCGO=gccgo
set GOWASM=
set AR=ar
set CC=gcc
set CXX=g++
set CGO_ENABLED=0
set GOMOD=C:\Users\Administrator\go\src\wasm\go.mod
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2
set PKG_CONFIG=pkg-config
set GOGCCFLAGS=-fPIC -fmessage-length=0 -fdebug-prefix-map=C:\Users\Administrator\AppData\Local\Temp\go-build171043022=/tmp/go-build -gno-record-gcc-switches

What did you do?

I want hide my http request in go-wasm as the js-lib, my js code call go-wasm do the http request. See my demo as below:

$ main.go:
func main() {
	fmt.Println("Start go...")
	goObj := js.Global().Get("goObj")
	if goObj == js.Undefined() {
		fmt.Println("No goObj Object in global.")
		return
	}
	goObj.Set("version", version)
	goObj.Set("httpByGo", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		fmt.Println("Start httpByGo...")
		resChan := make(chan interface{})
		go func() {
			fmt.Println("Start http...")
			resp, err := http.Get("http://localhost:3000/demo.json")
			fmt.Println("11111111111111")
			defer resp.Body.Close()
			fmt.Println("22222222222222")
			body, err := ioutil.ReadAll(resp.Body)
			fmt.Println("3333333333333")
			log.Fatal(err.Error())
			resChan <- body
			fmt.Println("4444444444444")
		}()
		fmt.Println("5555555555555555")
		res := <- resChan
		fmt.Println("6666666666666666")
		return res
	}))

	wg := sync.WaitGroup{}
	wg.Add(1)
	wg.Wait()
}
$ web.js
$(function () {
  const go = new Go();
  WebAssembly.instantiateStreaming(fetch("go.wasm"), go.importObject).then((result) => {
    go.run(result.instance);
    goObj.httpByGo();
  });
});
var goObj = {};
$ demo.json
{
 "code": 200,
 "data": {
  "pageId": "1799490"
 }
}

What did you expect to see?

exprect to see the demo.json in console log.

What did you see instead?

log as below:

Start http...
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [semacquire]:
sync.runtime_Semacquire(0x41e178)
    C:/Go/src/runtime/sema.go:56 +0x2
sync.(*WaitGroup).Wait(0x41e170)
    C:/Go/src/sync/waitgroup.go:130 +0x10
main.main()
    C:/Users/Administrator/go/src/wasm/wasm.go:47 +0x13

goroutine 7 [select]:
net/http.(*Transport).RoundTrip(0x37ee60, 0x48c000, 0x0, 0x0, 0x0)
    C:/Go/src/net/http/roundtrip_js.go:169 +0x51
net/http.send(0x48c000, 0xde200, 0x37ee60, 0x0, 0x0, 0x0, 0x0, 0x40c040, 0x428b10, 0x1)
    C:/Go/src/net/http/client.go:250 +0x5c
net/http.(*Client).send(0x38b1e0, 0x48c000, 0x0, 0x0, 0x0, 0x40c040, 0x0, 0x1, 0x85100)
    C:/Go/src/net/http/client.go:174 +0x13
net/http.(*Client).do(0x38b1e0, 0x48c000, 0x0, 0x0, 0x0)
    C:/Go/src/net/http/client.go:641 +0x35
net/http.(*Client).Do(...)
    C:/Go/src/net/http/client.go:509
net/http.(*Client).Get(0x38b1e0, 0x965a9, 0x1f, 0x1, 0x1, 0xe)
    C:/Go/src/net/http/client.go:398 +0xe
net/http.Get(...)
    C:/Go/src/net/http/client.go:370
main.main.func1.1(0x4300c0)
    C:/Users/Administrator/go/src/wasm/main.go:29 +0x7
created by main.main.func1
    C:/Users/Administrator/go/src/wasm/main.go:27 +0x7
@odeke-em odeke-em changed the title go-wasm: deadlock when http.Get in syscall/js.FuncOf misc/wasm: deadlock from http.Get in syscall/js.FuncOf Feb 8, 2020
@odeke-em
Copy link
Member

odeke-em commented Feb 8, 2020

Thank you for reporting this issue @QFleet and welcome to the Go project!

Kindly pinging some WASM experts @neelance @cherrymui

@odeke-em odeke-em added arch-wasm WebAssembly issues NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. labels Feb 8, 2020
@QFleet
Copy link
Author

QFleet commented Feb 8, 2020

i move the http request into main(), not in syscall/js.FuncOf. i can get the response from console log. there is only the deadlock of http request in syscall/js.FuncOf

@cherrymui
Copy link
Member

The doc of FuncOf, https://golang.org/pkg/syscall/js/#FuncOf , said that blocking operations in the wrapped function will block the event loop. So I think you want to move blocking operations out of FuncOf, e.g. to main as you did.

@odeke-em
Copy link
Member

odeke-em commented Feb 8, 2020

Thank you for the investigation and diagnosis @cherrymui!

@QFleet this is working as intended and as recommended by that docs and reiterated by @cherrymui, please don’t put blocking operations in FuncOf.

I shall close this issue but please don’t hesitate to open others, and I hope to see you even more in that community. Thank you.

@odeke-em odeke-em closed this as completed Feb 8, 2020
@QFleet
Copy link
Author

QFleet commented Feb 9, 2020

thanks a lot @cherrymui @odeke-em

background: i have huge amounts of data with different source by socket

FuncOf is the simple collector for params in my opinion, the real logic in another goroute(maybe many)? do you have any idea?
DONOT put blocking operations in FuncOf means a lot of work for me.

there are 2 methods in my opinion as below:

  1. By document.emit (during the whole, additional: lock...unlock...lock...unlock)
    i. get params in FuncOf
    ii. send them to another goroute
    iii. If get the result, i have to send return by DOM.emit("event"), no return of FuncOf.

  2. Use web-http-request(ajax) in FuncOf. (work on js-thread)

goObj.Set("getDataByHttp1", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		jquery := js.Global().Get("$")
		if jquery != js.Undefined() {
			rtn := jquery.Get("ajax").Invoke("http://localhost:3000/demo.json")
			return rtn
		}
		return nil
	}))

@QFleet
Copy link
Author

QFleet commented Feb 9, 2020

@cherrymui
i make a method, and i can get my data from socket of "FuncOf". and it looks like simple. h5 js code will get data by then.

$ web.js
goObj.httpGo().then(function(res){ console.log(res);});

thanks for your reply,

@golang golang locked and limited conversation to collaborators Feb 8, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
arch-wasm WebAssembly issues FrozenDueToAge NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
Development

No branches or pull requests

4 participants