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  }