github.com/hikaru7719/go@v0.0.0-20181025140707-c8b2ac68906a/src/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 := scheduleCallback(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  		clearScheduledCallback(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 waitingForCallback *g
   138  
   139  // sleepUntilCallback puts the current goroutine to sleep until a callback is triggered.
   140  // It is currently only used by the callback routine of the syscall/js package.
   141  //go:linkname sleepUntilCallback syscall/js.sleepUntilCallback
   142  func sleepUntilCallback() {
   143  	waitingForCallback = getg()
   144  	gopark(nil, nil, waitReasonZero, traceEvNone, 1)
   145  	waitingForCallback = nil
   146  }
   147  
   148  // pauseSchedulerUntilCallback gets called from the scheduler and pauses the execution
   149  // of Go's WebAssembly code until a callback is triggered. Then it checks for note timeouts
   150  // and resumes goroutines that are waiting for a callback.
   151  func pauseSchedulerUntilCallback() bool {
   152  	if waitingForCallback == nil && len(notesWithTimeout) == 0 {
   153  		return false
   154  	}
   155  
   156  	pause()
   157  	checkTimeouts()
   158  	if waitingForCallback != nil {
   159  		goready(waitingForCallback, 1)
   160  	}
   161  	return true
   162  }
   163  
   164  // pause pauses the execution of Go's WebAssembly code until a callback is triggered.
   165  func pause()
   166  
   167  // scheduleCallback tells the WebAssembly environment to trigger a callback after ms milliseconds.
   168  // It returns a timer id that can be used with clearScheduledCallback.
   169  func scheduleCallback(ms int64) int32
   170  
   171  // clearScheduledCallback clears a callback scheduled by scheduleCallback.
   172  func clearScheduledCallback(id int32)