github.com/megatontech/mynoteforgo@v0.0.0-20200507084910-5d0c6ea6e890/源码/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 // Waiting for a mutex is implemented by allowing other goroutines 15 // to run until the mutex gets unlocked. 16 17 const ( 18 mutex_unlocked = 0 19 mutex_locked = 1 20 21 note_cleared = 0 22 note_woken = 1 23 note_timeout = 2 24 25 active_spin = 4 26 active_spin_cnt = 30 27 passive_spin = 1 28 ) 29 30 func lock(l *mutex) { 31 for l.key == mutex_locked { 32 mcall(gosched_m) 33 } 34 l.key = mutex_locked 35 } 36 37 func unlock(l *mutex) { 38 if l.key == mutex_unlocked { 39 throw("unlock of unlocked lock") 40 } 41 l.key = mutex_unlocked 42 } 43 44 // One-time notifications. 45 46 type noteWithTimeout struct { 47 gp *g 48 deadline int64 49 } 50 51 var ( 52 notes = make(map[*note]*g) 53 notesWithTimeout = make(map[*note]noteWithTimeout) 54 ) 55 56 func noteclear(n *note) { 57 n.key = note_cleared 58 } 59 60 func notewakeup(n *note) { 61 // gp := getg() 62 if n.key == note_woken { 63 throw("notewakeup - double wakeup") 64 } 65 cleared := n.key == note_cleared 66 n.key = note_woken 67 if cleared { 68 goready(notes[n], 1) 69 } 70 } 71 72 func notesleep(n *note) { 73 throw("notesleep not supported by js") 74 } 75 76 func notetsleep(n *note, ns int64) bool { 77 throw("notetsleep not supported by js") 78 return false 79 } 80 81 // same as runtime·notetsleep, but called on user g (not g0) 82 func notetsleepg(n *note, ns int64) bool { 83 gp := getg() 84 if gp == gp.m.g0 { 85 throw("notetsleepg on g0") 86 } 87 88 if ns >= 0 { 89 deadline := nanotime() + ns 90 delay := ns/1000000 + 1 // round up 91 if delay > 1<<31-1 { 92 delay = 1<<31 - 1 // cap to max int32 93 } 94 95 id := scheduleTimeoutEvent(delay) 96 mp := acquirem() 97 notes[n] = gp 98 notesWithTimeout[n] = noteWithTimeout{gp: gp, deadline: deadline} 99 releasem(mp) 100 101 gopark(nil, nil, waitReasonSleep, traceEvNone, 1) 102 103 clearTimeoutEvent(id) // note might have woken early, clear timeout 104 mp = acquirem() 105 delete(notes, n) 106 delete(notesWithTimeout, n) 107 releasem(mp) 108 109 return n.key == note_woken 110 } 111 112 for n.key != note_woken { 113 mp := acquirem() 114 notes[n] = gp 115 releasem(mp) 116 117 gopark(nil, nil, waitReasonZero, traceEvNone, 1) 118 119 mp = acquirem() 120 delete(notes, n) 121 releasem(mp) 122 } 123 return true 124 } 125 126 // checkTimeouts resumes goroutines that are waiting on a note which has reached its deadline. 127 func checkTimeouts() { 128 now := nanotime() 129 for n, nt := range notesWithTimeout { 130 if n.key == note_cleared && now >= nt.deadline { 131 n.key = note_timeout 132 goready(nt.gp, 1) 133 } 134 } 135 } 136 137 var returnedEventHandler *g 138 139 func init() { 140 // At the toplevel we need an extra goroutine that handles asynchronous events. 141 initg := getg() 142 go func() { 143 returnedEventHandler = getg() 144 goready(initg, 1) 145 146 gopark(nil, nil, waitReasonZero, traceEvNone, 1) 147 returnedEventHandler = nil 148 149 pause(getcallersp() - 16) 150 }() 151 gopark(nil, nil, waitReasonZero, traceEvNone, 1) 152 } 153 154 // beforeIdle gets called by the scheduler if no goroutine is awake. 155 // We resume the event handler (if available) which will pause the execution. 156 func beforeIdle() bool { 157 if returnedEventHandler != nil { 158 goready(returnedEventHandler, 1) 159 return true 160 } 161 return false 162 } 163 164 // pause sets SP to newsp and pauses the execution of Go's WebAssembly code until an event is triggered. 165 func pause(newsp uintptr) 166 167 // scheduleTimeoutEvent tells the WebAssembly environment to trigger an event after ms milliseconds. 168 // It returns a timer id that can be used with clearTimeoutEvent. 169 func scheduleTimeoutEvent(ms int64) int32 170 171 // clearTimeoutEvent clears a timeout event scheduled by scheduleTimeoutEvent. 172 func clearTimeoutEvent(id int32) 173 174 func handleEvent() { 175 prevReturnedEventHandler := returnedEventHandler 176 returnedEventHandler = nil 177 178 checkTimeouts() 179 eventHandler() 180 181 returnedEventHandler = getg() 182 gopark(nil, nil, waitReasonZero, traceEvNone, 1) 183 184 returnedEventHandler = prevReturnedEventHandler 185 186 pause(getcallersp() - 16) 187 } 188 189 var eventHandler func() 190 191 //go:linkname setEventHandler syscall/js.setEventHandler 192 func setEventHandler(fn func()) { 193 eventHandler = fn 194 }