github.com/x04/go/src@v0.0.0-20200202162449-3d481ceb3525/runtime/lock_js.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // +build js,wasm 6 7 package runtime 8 9 import ( 10 _ "github.com/x04/go/src/unsafe" 11 ) 12 13 // js/wasm has no support for threads yet. There is no preemption. 14 15 const ( 16 mutex_unlocked = 0 17 mutex_locked = 1 18 19 note_cleared = 0 20 note_woken = 1 21 note_timeout = 2 22 23 active_spin = 4 24 active_spin_cnt = 30 25 passive_spin = 1 26 ) 27 28 func lock(l *mutex) { 29 if l.key == mutex_locked { 30 // js/wasm is single-threaded so we should never 31 // observe this. 32 throw("self deadlock") 33 } 34 gp := getg() 35 if gp.m.locks < 0 { 36 throw("lock count") 37 } 38 gp.m.locks++ 39 l.key = mutex_locked 40 } 41 42 func unlock(l *mutex) { 43 if l.key == mutex_unlocked { 44 throw("unlock of unlocked lock") 45 } 46 gp := getg() 47 gp.m.locks-- 48 if gp.m.locks < 0 { 49 throw("lock count") 50 } 51 l.key = mutex_unlocked 52 } 53 54 // One-time notifications. 55 56 type noteWithTimeout struct { 57 gp *g 58 deadline int64 59 } 60 61 var ( 62 notes = make(map[*note]*g) 63 notesWithTimeout = make(map[*note]noteWithTimeout) 64 ) 65 66 func noteclear(n *note) { 67 n.key = note_cleared 68 } 69 70 func notewakeup(n *note) { 71 // gp := getg() 72 if n.key == note_woken { 73 throw("notewakeup - double wakeup") 74 } 75 cleared := n.key == note_cleared 76 n.key = note_woken 77 if cleared { 78 goready(notes[n], 1) 79 } 80 } 81 82 func notesleep(n *note) { 83 throw("notesleep not supported by js") 84 } 85 86 func notetsleep(n *note, ns int64) bool { 87 throw("notetsleep not supported by js") 88 return false 89 } 90 91 // same as runtimeĀ·notetsleep, but called on user g (not g0) 92 func notetsleepg(n *note, ns int64) bool { 93 gp := getg() 94 if gp == gp.m.g0 { 95 throw("notetsleepg on g0") 96 } 97 98 if ns >= 0 { 99 deadline := nanotime() + ns 100 delay := ns/1000000 + 1 // round up 101 if delay > 1<<31-1 { 102 delay = 1<<31 - 1 // cap to max int32 103 } 104 105 id := scheduleTimeoutEvent(delay) 106 mp := acquirem() 107 notes[n] = gp 108 notesWithTimeout[n] = noteWithTimeout{gp: gp, deadline: deadline} 109 releasem(mp) 110 111 gopark(nil, nil, waitReasonSleep, traceEvNone, 1) 112 113 clearTimeoutEvent(id) // note might have woken early, clear timeout 114 clearIdleID() 115 116 mp = acquirem() 117 delete(notes, n) 118 delete(notesWithTimeout, n) 119 releasem(mp) 120 121 return n.key == note_woken 122 } 123 124 for n.key != note_woken { 125 mp := acquirem() 126 notes[n] = gp 127 releasem(mp) 128 129 gopark(nil, nil, waitReasonZero, traceEvNone, 1) 130 131 mp = acquirem() 132 delete(notes, n) 133 releasem(mp) 134 } 135 return true 136 } 137 138 // checkTimeouts resumes goroutines that are waiting on a note which has reached its deadline. 139 func checkTimeouts() { 140 now := nanotime() 141 for n, nt := range notesWithTimeout { 142 if n.key == note_cleared && now >= nt.deadline { 143 n.key = note_timeout 144 goready(nt.gp, 1) 145 } 146 } 147 } 148 149 // events is a stack of calls from JavaScript into Go. 150 var events []*event 151 152 type event struct { 153 // g was the active goroutine when the call from JavaScript occurred. 154 // It needs to be active when returning to JavaScript. 155 gp *g 156 // returned reports whether the event handler has returned. 157 // When all goroutines are idle and the event handler has returned, 158 // then g gets resumed and returns the execution to JavaScript. 159 returned bool 160 } 161 162 // The timeout event started by beforeIdle. 163 var idleID int32 164 165 // beforeIdle gets called by the scheduler if no goroutine is awake. 166 // If we are not already handling an event, then we pause for an async event. 167 // If an event handler returned, we resume it and it will pause the execution. 168 func beforeIdle(delay int64) bool { 169 if delay > 0 { 170 clearIdleID() 171 if delay < 1e6 { 172 delay = 1 173 } else if delay < 1e15 { 174 delay = delay / 1e6 175 } else { 176 // An arbitrary cap on how long to wait for a timer. 177 // 1e9 ms == ~11.5 days. 178 delay = 1e9 179 } 180 idleID = scheduleTimeoutEvent(delay) 181 } 182 183 if len(events) == 0 { 184 go handleAsyncEvent() 185 return true 186 } 187 188 e := events[len(events)-1] 189 if e.returned { 190 goready(e.gp, 1) 191 return true 192 } 193 return false 194 } 195 196 func handleAsyncEvent() { 197 pause(getcallersp() - 16) 198 } 199 200 // clearIdleID clears our record of the timeout started by beforeIdle. 201 func clearIdleID() { 202 if idleID != 0 { 203 clearTimeoutEvent(idleID) 204 idleID = 0 205 } 206 } 207 208 // pause sets SP to newsp and pauses the execution of Go's WebAssembly code until an event is triggered. 209 func pause(newsp uintptr) 210 211 // scheduleTimeoutEvent tells the WebAssembly environment to trigger an event after ms milliseconds. 212 // It returns a timer id that can be used with clearTimeoutEvent. 213 func scheduleTimeoutEvent(ms int64) int32 214 215 // clearTimeoutEvent clears a timeout event scheduled by scheduleTimeoutEvent. 216 func clearTimeoutEvent(id int32) 217 218 // handleEvent gets invoked on a call from JavaScript into Go. It calls the event handler of the syscall/js package 219 // and then parks the handler goroutine to allow other goroutines to run before giving execution back to JavaScript. 220 // When no other goroutine is awake any more, beforeIdle resumes the handler goroutine. Now that the same goroutine 221 // is running as was running when the call came in from JavaScript, execution can be safely passed back to JavaScript. 222 func handleEvent() { 223 e := &event{ 224 gp: getg(), 225 returned: false, 226 } 227 events = append(events, e) 228 229 eventHandler() 230 231 clearIdleID() 232 233 // wait until all goroutines are idle 234 e.returned = true 235 gopark(nil, nil, waitReasonZero, traceEvNone, 1) 236 237 events[len(events)-1] = nil 238 events = events[:len(events)-1] 239 240 // return execution to JavaScript 241 pause(getcallersp() - 16) 242 } 243 244 var eventHandler func() 245 246 //go:linkname setEventHandler syscall/js.setEventHandler 247 func setEventHandler(fn func()) { 248 eventHandler = fn 249 }