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