You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I am using wasm to create browser event handling callbacks.
These callbacks need to access mutable state in an exclusive manner.
Hence the wrapped functions (run in response to any triggered event) lock the mutex that protect the mutable state.
A critical section should be established for the duration of the callback call (so until the callback returns)
package main
import (
"strconv""sync""syscall/js""log
)
// GOOS=js GOARCH=wasm go build -o server/assets/app.wasmvarmutex=&sync.Mutex{}
// Mutable is a variable that is protected for access by multiple goroutines // (we may want to access it asynchronously)// It is modified in response to user actions in the browser.// As such, it models the UI tree that would get modified by user initiated events.// Thus, different event handlers need to take the lock before modifying itvarMutableintvarevtcounter=js.Global().Get("document").Call("createElement", "div")
funcAddEventListener(eventstring, target js.Value){
cb:=js.FuncOf(func(this js.Value, args []js.Value)any{
// RETRIEVING EVENT INFOevt:=args[0]
typ:=evt.Get("type").String()
log.Print("== HANDLING "+typ+" EVENT ==\n")
// EVENT HANDLINGlog.Print("LOCKING\n")
b:=mutex.TryLock() // locking here as we need to access Mutableif!b{
log.Print("ERROR: UNLOCKING phase seems to have been skipped in previous handler. Wrapped Functions are prempted, interleaved?")
mutex.Lock() // just to cause the deadlock
}
log.Print("LOCKED\n")
Mutable++evtcounter.Set("textContent", "# of event triggers: "+strconv.Itoa(Mutable))
evtcounter.Call("focus")
log.Print("UNLOCKING\n")
mutex.Unlock() // Unlocking needs to happen before another callback starts.log.Print("UNLOCKED ... ready for locking\n")
returnnil
})
target.Call("addEventListener", event, cb)
}
funcmain(){
body:=js.Global().Get("document").Get("body")
button:=js.Global().Get("document").Call("createElement", "btn")
button.Set("textContent","click me")
evtcounter.Call("setAttribute", "tabindex","-1") // makes it focusable programmaticallybody.Call("appendChild",evtcounter)
body.Call("appendChild",button)
AddEventListener("click",button)
AddEventListener("focusin",body)
c:=make(chanstruct{}, 0)
<-c
}
What did you expect to see?
I expected to see the mutex being unlocked before any other wasm-based event handling callback can run. Essentially, I was expecting each callback to finish running before the next one.
What did you see instead?
The functions seem to run in an interleaved manner. Which is strange because javascript event handlers should run synchronously, not concurrently.
The lock is not released before another callback runs and attempts to take it resulting in a deadlock.
Oh, that must be it, thanks! I missed that completely.
I thought I could avoid the need for reentrant locks but seems that I am still knocking my head against this.
Sorry for the bother and thanks again.
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
I haven't installed 1.19 yet.
What operating system and processor architecture are you using (
go env
)?linux, amd64
go env
OutputWhat did you do?
I am using wasm to create browser event handling callbacks.
These callbacks need to access mutable state in an exclusive manner.
Hence the wrapped functions (run in response to any triggered event) lock the mutex that protect the mutable state.
A critical section should be established for the duration of the callback call (so until the callback returns)
A full reproducer can be found repo link
What did you expect to see?
I expected to see the mutex being unlocked before any other wasm-based event handling callback can run. Essentially, I was expecting each callback to finish running before the next one.
What did you see instead?
The functions seem to run in an interleaved manner. Which is strange because javascript event handlers should run synchronously, not concurrently.
The lock is not released before another callback runs and attempts to take it resulting in a deadlock.
I tracked the problem to:
eventHandler function in runtime.handleEvent
Which is initialized to be:
syscall/js js.handleEvent()
Which run the wrapped function f.
The text was updated successfully, but these errors were encountered: