| // Copyright 2018 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| // +build js,wasm | |
| package runtime | |
| import ( | |
| _ "unsafe" | |
| ) | |
| // js/wasm has no support for threads yet. There is no preemption. | |
| // Waiting for a mutex is implemented by allowing other goroutines | |
| // to run until the mutex gets unlocked. | |
| const ( | |
| mutex_unlocked = 0 | |
| mutex_locked = 1 | |
| note_cleared = 0 | |
| note_woken = 1 | |
| note_timeout = 2 | |
| active_spin = 4 | |
| active_spin_cnt = 30 | |
| passive_spin = 1 | |
| ) | |
| func lock(l *mutex) { | |
| for l.key == mutex_locked { | |
| mcall(gosched_m) | |
| } | |
| l.key = mutex_locked | |
| } | |
| func unlock(l *mutex) { | |
| if l.key == mutex_unlocked { | |
| throw("unlock of unlocked lock") | |
| } | |
| l.key = mutex_unlocked | |
| } | |
| // One-time notifications. | |
| type noteWithTimeout struct { | |
| gp *g | |
| deadline int64 | |
| } | |
| var ( | |
| notes = make(map[*note]*g) | |
| notesWithTimeout = make(map[*note]noteWithTimeout) | |
| ) | |
| func noteclear(n *note) { | |
| n.key = note_cleared | |
| } | |
| func notewakeup(n *note) { | |
| // gp := getg() | |
| if n.key == note_woken { | |
| throw("notewakeup - double wakeup") | |
| } | |
| cleared := n.key == note_cleared | |
| n.key = note_woken | |
| if cleared { | |
| goready(notes[n], 1) | |
| } | |
| } | |
| func notesleep(n *note) { | |
| throw("notesleep not supported by js") | |
| } | |
| func notetsleep(n *note, ns int64) bool { | |
| throw("notetsleep not supported by js") | |
| return false | |
| } | |
| // same as runtimeĀ·notetsleep, but called on user g (not g0) | |
| func notetsleepg(n *note, ns int64) bool { | |
| gp := getg() | |
| if gp == gp.m.g0 { | |
| throw("notetsleepg on g0") | |
| } | |
| if ns >= 0 { | |
| deadline := nanotime() + ns | |
| delay := ns/1000000 + 1 // round up | |
| if delay > 1<<31-1 { | |
| delay = 1<<31 - 1 // cap to max int32 | |
| } | |
| id := scheduleCallback(delay) | |
| mp := acquirem() | |
| notes[n] = gp | |
| notesWithTimeout[n] = noteWithTimeout{gp: gp, deadline: deadline} | |
| releasem(mp) | |
| gopark(nil, nil, waitReasonSleep, traceEvNone, 1) | |
| clearScheduledCallback(id) // note might have woken early, clear timeout | |
| mp = acquirem() | |
| delete(notes, n) | |
| delete(notesWithTimeout, n) | |
| releasem(mp) | |
| return n.key == note_woken | |
| } | |
| for n.key != note_woken { | |
| mp := acquirem() | |
| notes[n] = gp | |
| releasem(mp) | |
| gopark(nil, nil, waitReasonZero, traceEvNone, 1) | |
| mp = acquirem() | |
| delete(notes, n) | |
| releasem(mp) | |
| } | |
| return true | |
| } | |
| // checkTimeouts resumes goroutines that are waiting on a note which has reached its deadline. | |
| func checkTimeouts() { | |
| now := nanotime() | |
| for n, nt := range notesWithTimeout { | |
| if n.key == note_cleared && now > nt.deadline { | |
| n.key = note_timeout | |
| goready(nt.gp, 1) | |
| } | |
| } | |
| } | |
| var waitingForCallback *g | |
| // sleepUntilCallback puts the current goroutine to sleep until a callback is triggered. | |
| // It is currently only used by the callback routine of the syscall/js package. | |
| //go:linkname sleepUntilCallback syscall/js.sleepUntilCallback | |
| func sleepUntilCallback() { | |
| waitingForCallback = getg() | |
| gopark(nil, nil, waitReasonZero, traceEvNone, 1) | |
| waitingForCallback = nil | |
| } | |
| // pauseSchedulerUntilCallback gets called from the scheduler and pauses the execution | |
| // of Go's WebAssembly code until a callback is triggered. Then it checks for note timeouts | |
| // and resumes goroutines that are waiting for a callback. | |
| func pauseSchedulerUntilCallback() bool { | |
| if waitingForCallback == nil && len(notesWithTimeout) == 0 { | |
| return false | |
| } | |
| pause() | |
| checkTimeouts() | |
| if waitingForCallback != nil { | |
| goready(waitingForCallback, 1) | |
| } | |
| return true | |
| } | |
| // pause pauses the execution of Go's WebAssembly code until a callback is triggered. | |
| func pause() | |
| // scheduleCallback tells the WebAssembly environment to trigger a callback after ms milliseconds. | |
| // It returns a timer id that can be used with clearScheduledCallback. | |
| func scheduleCallback(ms int64) int32 | |
| // clearScheduledCallback clears a callback scheduled by scheduleCallback. | |
| func clearScheduledCallback(id int32) |